thuban 0.3.4 → 0.4.0

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 (918) hide show
  1. package/README.md +2 -0
  2. package/dist/README.md +2 -0
  3. package/dist/cli.js +1 -1
  4. package/dist/packages/crucible/.golden/golden-master.json +414 -0
  5. package/dist/packages/crucible/badge-generator.js +1 -0
  6. package/dist/packages/crucible/banner.js +1 -0
  7. package/dist/packages/crucible/ci-integration.js +1 -0
  8. package/dist/packages/crucible/golden-master.js +1 -0
  9. package/dist/packages/crucible/index.js +1 -0
  10. package/dist/packages/crucible/kenny-mode.js +1 -0
  11. package/dist/packages/crucible/leaderboard.js +1 -0
  12. package/dist/packages/crucible/mutation-engine.js +1 -0
  13. package/dist/packages/crucible/pattern-learner.js +1 -0
  14. package/dist/packages/crucible/report-generator.js +1 -0
  15. package/dist/packages/crucible/rule-loader.js +1 -0
  16. package/dist/packages/crucible/seeding-engine.js +1 -0
  17. package/dist/packages/crucible/seeds/csharp/seed-001.cs +28 -0
  18. package/dist/packages/crucible/seeds/csharp/seed-002.cs +24 -0
  19. package/dist/packages/crucible/seeds/csharp/seed-003.cs +30 -0
  20. package/dist/packages/crucible/seeds/csharp/seed-004.cs +31 -0
  21. package/dist/packages/crucible/seeds/csharp/seed-005.cs +28 -0
  22. package/dist/packages/crucible/seeds/csharp/seed-006.cs +25 -0
  23. package/dist/packages/crucible/seeds/csharp/seed-007.cs +34 -0
  24. package/dist/packages/crucible/seeds/csharp/seed-008.cs +23 -0
  25. package/dist/packages/crucible/seeds/csharp/seed-009.cs +27 -0
  26. package/dist/packages/crucible/seeds/csharp/seed-010.cs +29 -0
  27. package/dist/packages/crucible/seeds/csharp/seed-011.cs +32 -0
  28. package/dist/packages/crucible/seeds/csharp/seed-012.cs +27 -0
  29. package/dist/packages/crucible/seeds/csharp/seed-013.cs +27 -0
  30. package/dist/packages/crucible/seeds/csharp/seed-014.cs +26 -0
  31. package/dist/packages/crucible/seeds/csharp/seed-015.cs +33 -0
  32. package/dist/packages/crucible/seeds/csharp/seed-016.cs +25 -0
  33. package/dist/packages/crucible/seeds/csharp/seed-017.cs +22 -0
  34. package/dist/packages/crucible/seeds/csharp/seed-018.cs +26 -0
  35. package/dist/packages/crucible/seeds/csharp/seed-019.cs +25 -0
  36. package/dist/packages/crucible/seeds/csharp/seed-020.cs +22 -0
  37. package/dist/packages/crucible/seeds/csharp/seed-021.cs +28 -0
  38. package/dist/packages/crucible/seeds/csharp/seed-022.cs +25 -0
  39. package/dist/packages/crucible/seeds/csharp/seed-023.cs +33 -0
  40. package/dist/packages/crucible/seeds/csharp/seed-024.cs +31 -0
  41. package/dist/packages/crucible/seeds/csharp/seed-025.cs +31 -0
  42. package/dist/packages/crucible/seeds/csharp/seed-026.cs +25 -0
  43. package/dist/packages/crucible/seeds/csharp/seed-027.cs +29 -0
  44. package/dist/packages/crucible/seeds/csharp/seed-028.cs +25 -0
  45. package/dist/packages/crucible/seeds/csharp/seed-029.cs +27 -0
  46. package/dist/packages/crucible/seeds/csharp/seed-030.cs +27 -0
  47. package/dist/packages/crucible/seeds/csharp/seed-031.cs +28 -0
  48. package/dist/packages/crucible/seeds/csharp/seed-032.cs +30 -0
  49. package/dist/packages/crucible/seeds/csharp/seed-033.cs +27 -0
  50. package/dist/packages/crucible/seeds/csharp/seed-034.cs +25 -0
  51. package/dist/packages/crucible/seeds/csharp/seed-035.cs +32 -0
  52. package/dist/packages/crucible/seeds/csharp/seed-036.cs +28 -0
  53. package/dist/packages/crucible/seeds/csharp/seed-037.cs +29 -0
  54. package/dist/packages/crucible/seeds/csharp/seed-038.cs +28 -0
  55. package/dist/packages/crucible/seeds/csharp/seed-039.cs +29 -0
  56. package/dist/packages/crucible/seeds/csharp/seed-040.cs +28 -0
  57. package/dist/packages/crucible/seeds/csharp/seed-041.cs +33 -0
  58. package/dist/packages/crucible/seeds/csharp/seed-042.cs +27 -0
  59. package/dist/packages/crucible/seeds/csharp/seed-043.cs +27 -0
  60. package/dist/packages/crucible/seeds/csharp/seed-044.cs +25 -0
  61. package/dist/packages/crucible/seeds/csharp/seed-045.cs +30 -0
  62. package/dist/packages/crucible/seeds/csharp/seed-046.cs +29 -0
  63. package/dist/packages/crucible/seeds/csharp/seed-047.cs +26 -0
  64. package/dist/packages/crucible/seeds/csharp/seed-048.cs +31 -0
  65. package/dist/packages/crucible/seeds/csharp/seed-049.cs +27 -0
  66. package/dist/packages/crucible/seeds/csharp/seed-050.cs +30 -0
  67. package/dist/packages/crucible/seeds/csharp/seed-051.cs +34 -0
  68. package/dist/packages/crucible/seeds/csharp/seed-052.cs +36 -0
  69. package/dist/packages/crucible/seeds/go/seed-001.go +29 -0
  70. package/dist/packages/crucible/seeds/go/seed-002.go +27 -0
  71. package/dist/packages/crucible/seeds/go/seed-003.go +31 -0
  72. package/dist/packages/crucible/seeds/go/seed-004.go +27 -0
  73. package/dist/packages/crucible/seeds/go/seed-005.go +27 -0
  74. package/dist/packages/crucible/seeds/go/seed-006.go +29 -0
  75. package/dist/packages/crucible/seeds/go/seed-007.go +24 -0
  76. package/dist/packages/crucible/seeds/go/seed-008.go +25 -0
  77. package/dist/packages/crucible/seeds/go/seed-009.go +30 -0
  78. package/dist/packages/crucible/seeds/go/seed-010.go +35 -0
  79. package/dist/packages/crucible/seeds/go/seed-011.go +24 -0
  80. package/dist/packages/crucible/seeds/go/seed-012.go +24 -0
  81. package/dist/packages/crucible/seeds/go/seed-013.go +31 -0
  82. package/dist/packages/crucible/seeds/go/seed-014.go +35 -0
  83. package/dist/packages/crucible/seeds/go/seed-015.go +25 -0
  84. package/dist/packages/crucible/seeds/go/seed-016.go +19 -0
  85. package/dist/packages/crucible/seeds/go/seed-017.go +23 -0
  86. package/dist/packages/crucible/seeds/go/seed-018.go +23 -0
  87. package/dist/packages/crucible/seeds/go/seed-019.go +22 -0
  88. package/dist/packages/crucible/seeds/go/seed-020.go +31 -0
  89. package/dist/packages/crucible/seeds/go/seed-021.go +22 -0
  90. package/dist/packages/crucible/seeds/go/seed-022.go +29 -0
  91. package/dist/packages/crucible/seeds/go/seed-023.go +26 -0
  92. package/dist/packages/crucible/seeds/go/seed-024.go +27 -0
  93. package/dist/packages/crucible/seeds/go/seed-025.go +23 -0
  94. package/dist/packages/crucible/seeds/go/seed-026.go +29 -0
  95. package/dist/packages/crucible/seeds/go/seed-027.go +26 -0
  96. package/dist/packages/crucible/seeds/go/seed-028.go +23 -0
  97. package/dist/packages/crucible/seeds/go/seed-029.go +28 -0
  98. package/dist/packages/crucible/seeds/go/seed-030.go +27 -0
  99. package/dist/packages/crucible/seeds/go/seed-031.go +28 -0
  100. package/dist/packages/crucible/seeds/go/seed-032.go +27 -0
  101. package/dist/packages/crucible/seeds/go/seed-033.go +29 -0
  102. package/dist/packages/crucible/seeds/go/seed-034.go +22 -0
  103. package/dist/packages/crucible/seeds/go/seed-035.go +32 -0
  104. package/dist/packages/crucible/seeds/go/seed-036.go +27 -0
  105. package/dist/packages/crucible/seeds/go/seed-037.go +27 -0
  106. package/dist/packages/crucible/seeds/go/seed-038.go +27 -0
  107. package/dist/packages/crucible/seeds/go/seed-039.go +26 -0
  108. package/dist/packages/crucible/seeds/go/seed-040.go +37 -0
  109. package/dist/packages/crucible/seeds/go/seed-041.go +23 -0
  110. package/dist/packages/crucible/seeds/go/seed-042.go +32 -0
  111. package/dist/packages/crucible/seeds/go/seed-043.go +35 -0
  112. package/dist/packages/crucible/seeds/go/seed-044.go +25 -0
  113. package/dist/packages/crucible/seeds/go/seed-045.go +26 -0
  114. package/dist/packages/crucible/seeds/go/seed-046.go +34 -0
  115. package/dist/packages/crucible/seeds/go/seed-047.go +26 -0
  116. package/dist/packages/crucible/seeds/go/seed-048.go +29 -0
  117. package/dist/packages/crucible/seeds/go/seed-049.go +24 -0
  118. package/dist/packages/crucible/seeds/go/seed-050.go +27 -0
  119. package/dist/packages/crucible/seeds/go/seed-051.go +38 -0
  120. package/dist/packages/crucible/seeds/go/seed-052.go +27 -0
  121. package/dist/packages/crucible/seeds/java/seed-001.java +23 -0
  122. package/dist/packages/crucible/seeds/java/seed-002.java +22 -0
  123. package/dist/packages/crucible/seeds/java/seed-003.java +28 -0
  124. package/dist/packages/crucible/seeds/java/seed-004.java +26 -0
  125. package/dist/packages/crucible/seeds/java/seed-005.java +33 -0
  126. package/dist/packages/crucible/seeds/java/seed-006.java +22 -0
  127. package/dist/packages/crucible/seeds/java/seed-007.java +21 -0
  128. package/dist/packages/crucible/seeds/java/seed-008.java +29 -0
  129. package/dist/packages/crucible/seeds/java/seed-009.java +24 -0
  130. package/dist/packages/crucible/seeds/java/seed-010.java +30 -0
  131. package/dist/packages/crucible/seeds/java/seed-011.java +25 -0
  132. package/dist/packages/crucible/seeds/java/seed-012.java +20 -0
  133. package/dist/packages/crucible/seeds/java/seed-013.java +26 -0
  134. package/dist/packages/crucible/seeds/java/seed-014.java +23 -0
  135. package/dist/packages/crucible/seeds/java/seed-015.java +25 -0
  136. package/dist/packages/crucible/seeds/java/seed-016.java +22 -0
  137. package/dist/packages/crucible/seeds/java/seed-017.java +24 -0
  138. package/dist/packages/crucible/seeds/java/seed-018.java +24 -0
  139. package/dist/packages/crucible/seeds/java/seed-019.java +38 -0
  140. package/dist/packages/crucible/seeds/java/seed-020.java +24 -0
  141. package/dist/packages/crucible/seeds/java/seed-021.java +22 -0
  142. package/dist/packages/crucible/seeds/java/seed-022.java +22 -0
  143. package/dist/packages/crucible/seeds/java/seed-023.java +22 -0
  144. package/dist/packages/crucible/seeds/java/seed-024.java +25 -0
  145. package/dist/packages/crucible/seeds/java/seed-025.java +27 -0
  146. package/dist/packages/crucible/seeds/java/seed-026.java +25 -0
  147. package/dist/packages/crucible/seeds/java/seed-027.java +22 -0
  148. package/dist/packages/crucible/seeds/java/seed-028.java +21 -0
  149. package/dist/packages/crucible/seeds/java/seed-029.java +28 -0
  150. package/dist/packages/crucible/seeds/java/seed-030.java +27 -0
  151. package/dist/packages/crucible/seeds/java/seed-031.java +23 -0
  152. package/dist/packages/crucible/seeds/java/seed-032.java +22 -0
  153. package/dist/packages/crucible/seeds/java/seed-033.java +24 -0
  154. package/dist/packages/crucible/seeds/java/seed-034.java +24 -0
  155. package/dist/packages/crucible/seeds/java/seed-035.java +29 -0
  156. package/dist/packages/crucible/seeds/java/seed-036.java +25 -0
  157. package/dist/packages/crucible/seeds/java/seed-037.java +30 -0
  158. package/dist/packages/crucible/seeds/java/seed-038.java +29 -0
  159. package/dist/packages/crucible/seeds/java/seed-039.java +26 -0
  160. package/dist/packages/crucible/seeds/java/seed-040.java +31 -0
  161. package/dist/packages/crucible/seeds/java/seed-041.java +20 -0
  162. package/dist/packages/crucible/seeds/java/seed-042.java +26 -0
  163. package/dist/packages/crucible/seeds/java/seed-043.java +31 -0
  164. package/dist/packages/crucible/seeds/java/seed-044.java +22 -0
  165. package/dist/packages/crucible/seeds/java/seed-045.java +25 -0
  166. package/dist/packages/crucible/seeds/java/seed-046.java +22 -0
  167. package/dist/packages/crucible/seeds/java/seed-047.java +26 -0
  168. package/dist/packages/crucible/seeds/java/seed-048.java +24 -0
  169. package/dist/packages/crucible/seeds/java/seed-049.java +28 -0
  170. package/dist/packages/crucible/seeds/java/seed-050.java +29 -0
  171. package/dist/packages/crucible/seeds/java/seed-051.java +25 -0
  172. package/dist/packages/crucible/seeds/java/seed-052.java +32 -0
  173. package/dist/packages/crucible/seeds/js/seed-001.js +27 -0
  174. package/dist/packages/crucible/seeds/js/seed-002.js +28 -0
  175. package/dist/packages/crucible/seeds/js/seed-003.js +30 -0
  176. package/dist/packages/crucible/seeds/js/seed-004.js +28 -0
  177. package/dist/packages/crucible/seeds/js/seed-005.js +31 -0
  178. package/dist/packages/crucible/seeds/js/seed-006.js +28 -0
  179. package/dist/packages/crucible/seeds/js/seed-007.js +26 -0
  180. package/dist/packages/crucible/seeds/js/seed-008.js +30 -0
  181. package/dist/packages/crucible/seeds/js/seed-009.js +30 -0
  182. package/dist/packages/crucible/seeds/js/seed-010.js +29 -0
  183. package/dist/packages/crucible/seeds/js/seed-011.js +34 -0
  184. package/dist/packages/crucible/seeds/js/seed-012.js +32 -0
  185. package/dist/packages/crucible/seeds/js/seed-013.js +29 -0
  186. package/dist/packages/crucible/seeds/js/seed-014.js +29 -0
  187. package/dist/packages/crucible/seeds/js/seed-015.js +31 -0
  188. package/dist/packages/crucible/seeds/js/seed-016.js +31 -0
  189. package/dist/packages/crucible/seeds/js/seed-017.js +30 -0
  190. package/dist/packages/crucible/seeds/js/seed-018.js +29 -0
  191. package/dist/packages/crucible/seeds/js/seed-019.js +32 -0
  192. package/dist/packages/crucible/seeds/js/seed-020.js +29 -0
  193. package/dist/packages/crucible/seeds/js/seed-021.js +31 -0
  194. package/dist/packages/crucible/seeds/js/seed-022.js +31 -0
  195. package/dist/packages/crucible/seeds/js/seed-023.js +29 -0
  196. package/dist/packages/crucible/seeds/js/seed-024.js +29 -0
  197. package/dist/packages/crucible/seeds/js/seed-025.js +34 -0
  198. package/dist/packages/crucible/seeds/js/seed-026.js +31 -0
  199. package/dist/packages/crucible/seeds/js/seed-027.js +31 -0
  200. package/dist/packages/crucible/seeds/js/seed-028.js +33 -0
  201. package/dist/packages/crucible/seeds/js/seed-029.js +33 -0
  202. package/dist/packages/crucible/seeds/js/seed-030.js +30 -0
  203. package/dist/packages/crucible/seeds/js/seed-031.js +31 -0
  204. package/dist/packages/crucible/seeds/js/seed-032.js +34 -0
  205. package/dist/packages/crucible/seeds/js/seed-033.js +33 -0
  206. package/dist/packages/crucible/seeds/js/seed-034.js +33 -0
  207. package/dist/packages/crucible/seeds/js/seed-035.js +30 -0
  208. package/dist/packages/crucible/seeds/js/seed-036.js +30 -0
  209. package/dist/packages/crucible/seeds/js/seed-037.js +29 -0
  210. package/dist/packages/crucible/seeds/js/seed-038.js +31 -0
  211. package/dist/packages/crucible/seeds/js/seed-039.js +34 -0
  212. package/dist/packages/crucible/seeds/js/seed-040.js +35 -0
  213. package/dist/packages/crucible/seeds/js/seed-041.js +32 -0
  214. package/dist/packages/crucible/seeds/js/seed-042.js +32 -0
  215. package/dist/packages/crucible/seeds/js/seed-043.js +27 -0
  216. package/dist/packages/crucible/seeds/js/seed-044.js +29 -0
  217. package/dist/packages/crucible/seeds/js/seed-045.js +33 -0
  218. package/dist/packages/crucible/seeds/js/seed-046.js +33 -0
  219. package/dist/packages/crucible/seeds/js/seed-047.js +29 -0
  220. package/dist/packages/crucible/seeds/js/seed-048.js +29 -0
  221. package/dist/packages/crucible/seeds/js/seed-049.js +34 -0
  222. package/dist/packages/crucible/seeds/js/seed-050.js +34 -0
  223. package/dist/packages/crucible/seeds/js/seed-051.js +30 -0
  224. package/dist/packages/crucible/seeds/js/seed-052.js +30 -0
  225. package/dist/packages/crucible/seeds/js/seed-053.js +26 -0
  226. package/dist/packages/crucible/seeds/js/seed-054.js +35 -0
  227. package/dist/packages/crucible/seeds/js/seed-055.js +33 -0
  228. package/dist/packages/crucible/seeds/kotlin/seed-001.kt +24 -0
  229. package/dist/packages/crucible/seeds/kotlin/seed-002.kt +27 -0
  230. package/dist/packages/crucible/seeds/kotlin/seed-003.kt +29 -0
  231. package/dist/packages/crucible/seeds/kotlin/seed-004.kt +24 -0
  232. package/dist/packages/crucible/seeds/kotlin/seed-005.kt +29 -0
  233. package/dist/packages/crucible/seeds/kotlin/seed-006.kt +21 -0
  234. package/dist/packages/crucible/seeds/kotlin/seed-007.kt +23 -0
  235. package/dist/packages/crucible/seeds/kotlin/seed-008.kt +25 -0
  236. package/dist/packages/crucible/seeds/kotlin/seed-009.kt +21 -0
  237. package/dist/packages/crucible/seeds/kotlin/seed-010.kt +28 -0
  238. package/dist/packages/crucible/seeds/kotlin/seed-011.kt +22 -0
  239. package/dist/packages/crucible/seeds/kotlin/seed-012.kt +25 -0
  240. package/dist/packages/crucible/seeds/kotlin/seed-013.kt +30 -0
  241. package/dist/packages/crucible/seeds/kotlin/seed-014.kt +27 -0
  242. package/dist/packages/crucible/seeds/kotlin/seed-015.kt +24 -0
  243. package/dist/packages/crucible/seeds/kotlin/seed-016.kt +28 -0
  244. package/dist/packages/crucible/seeds/kotlin/seed-017.kt +33 -0
  245. package/dist/packages/crucible/seeds/kotlin/seed-018.kt +31 -0
  246. package/dist/packages/crucible/seeds/kotlin/seed-019.kt +28 -0
  247. package/dist/packages/crucible/seeds/kotlin/seed-020.kt +31 -0
  248. package/dist/packages/crucible/seeds/kotlin/seed-021.kt +34 -0
  249. package/dist/packages/crucible/seeds/kotlin/seed-022.kt +29 -0
  250. package/dist/packages/crucible/seeds/kotlin/seed-023.kt +20 -0
  251. package/dist/packages/crucible/seeds/kotlin/seed-024.kt +23 -0
  252. package/dist/packages/crucible/seeds/kotlin/seed-025.kt +23 -0
  253. package/dist/packages/crucible/seeds/kotlin/seed-026.kt +26 -0
  254. package/dist/packages/crucible/seeds/kotlin/seed-027.kt +29 -0
  255. package/dist/packages/crucible/seeds/kotlin/seed-028.kt +24 -0
  256. package/dist/packages/crucible/seeds/kotlin/seed-029.kt +27 -0
  257. package/dist/packages/crucible/seeds/kotlin/seed-030.kt +26 -0
  258. package/dist/packages/crucible/seeds/kotlin/seed-031.kt +28 -0
  259. package/dist/packages/crucible/seeds/kotlin/seed-032.kt +29 -0
  260. package/dist/packages/crucible/seeds/kotlin/seed-033.kt +30 -0
  261. package/dist/packages/crucible/seeds/kotlin/seed-034.kt +26 -0
  262. package/dist/packages/crucible/seeds/kotlin/seed-035.kt +33 -0
  263. package/dist/packages/crucible/seeds/kotlin/seed-036.kt +25 -0
  264. package/dist/packages/crucible/seeds/kotlin/seed-037.kt +26 -0
  265. package/dist/packages/crucible/seeds/kotlin/seed-038.kt +25 -0
  266. package/dist/packages/crucible/seeds/kotlin/seed-039.kt +29 -0
  267. package/dist/packages/crucible/seeds/kotlin/seed-040.kt +28 -0
  268. package/dist/packages/crucible/seeds/kotlin/seed-041.kt +31 -0
  269. package/dist/packages/crucible/seeds/kotlin/seed-042.kt +27 -0
  270. package/dist/packages/crucible/seeds/kotlin/seed-043.kt +26 -0
  271. package/dist/packages/crucible/seeds/kotlin/seed-044.kt +27 -0
  272. package/dist/packages/crucible/seeds/kotlin/seed-045.kt +27 -0
  273. package/dist/packages/crucible/seeds/kotlin/seed-046.kt +33 -0
  274. package/dist/packages/crucible/seeds/kotlin/seed-047.kt +32 -0
  275. package/dist/packages/crucible/seeds/kotlin/seed-048.kt +20 -0
  276. package/dist/packages/crucible/seeds/kotlin/seed-049.kt +23 -0
  277. package/dist/packages/crucible/seeds/kotlin/seed-050.kt +19 -0
  278. package/dist/packages/crucible/seeds/php/seed-001.php +23 -0
  279. package/dist/packages/crucible/seeds/php/seed-002.php +18 -0
  280. package/dist/packages/crucible/seeds/php/seed-003.php +25 -0
  281. package/dist/packages/crucible/seeds/php/seed-004.php +21 -0
  282. package/dist/packages/crucible/seeds/php/seed-005.php +21 -0
  283. package/dist/packages/crucible/seeds/php/seed-006.php +21 -0
  284. package/dist/packages/crucible/seeds/php/seed-007.php +18 -0
  285. package/dist/packages/crucible/seeds/php/seed-008.php +19 -0
  286. package/dist/packages/crucible/seeds/php/seed-009.php +24 -0
  287. package/dist/packages/crucible/seeds/php/seed-010.php +24 -0
  288. package/dist/packages/crucible/seeds/php/seed-011.php +18 -0
  289. package/dist/packages/crucible/seeds/php/seed-012.php +24 -0
  290. package/dist/packages/crucible/seeds/php/seed-013.php +23 -0
  291. package/dist/packages/crucible/seeds/php/seed-014.php +20 -0
  292. package/dist/packages/crucible/seeds/php/seed-015.php +18 -0
  293. package/dist/packages/crucible/seeds/php/seed-016.php +18 -0
  294. package/dist/packages/crucible/seeds/php/seed-017.php +18 -0
  295. package/dist/packages/crucible/seeds/php/seed-018.php +17 -0
  296. package/dist/packages/crucible/seeds/php/seed-019.php +24 -0
  297. package/dist/packages/crucible/seeds/php/seed-020.php +19 -0
  298. package/dist/packages/crucible/seeds/php/seed-021.php +22 -0
  299. package/dist/packages/crucible/seeds/php/seed-022.php +15 -0
  300. package/dist/packages/crucible/seeds/php/seed-023.php +24 -0
  301. package/dist/packages/crucible/seeds/php/seed-024.php +22 -0
  302. package/dist/packages/crucible/seeds/php/seed-025.php +20 -0
  303. package/dist/packages/crucible/seeds/php/seed-026.php +22 -0
  304. package/dist/packages/crucible/seeds/php/seed-027.php +15 -0
  305. package/dist/packages/crucible/seeds/php/seed-028.php +15 -0
  306. package/dist/packages/crucible/seeds/php/seed-029.php +21 -0
  307. package/dist/packages/crucible/seeds/php/seed-030.php +21 -0
  308. package/dist/packages/crucible/seeds/php/seed-031.php +23 -0
  309. package/dist/packages/crucible/seeds/php/seed-032.php +18 -0
  310. package/dist/packages/crucible/seeds/php/seed-033.php +18 -0
  311. package/dist/packages/crucible/seeds/php/seed-034.php +24 -0
  312. package/dist/packages/crucible/seeds/php/seed-035.php +22 -0
  313. package/dist/packages/crucible/seeds/php/seed-036.php +17 -0
  314. package/dist/packages/crucible/seeds/php/seed-037.php +19 -0
  315. package/dist/packages/crucible/seeds/php/seed-038.php +20 -0
  316. package/dist/packages/crucible/seeds/php/seed-039.php +15 -0
  317. package/dist/packages/crucible/seeds/php/seed-040.php +21 -0
  318. package/dist/packages/crucible/seeds/php/seed-041.php +20 -0
  319. package/dist/packages/crucible/seeds/php/seed-042.php +21 -0
  320. package/dist/packages/crucible/seeds/php/seed-043.php +21 -0
  321. package/dist/packages/crucible/seeds/php/seed-044.php +25 -0
  322. package/dist/packages/crucible/seeds/php/seed-045.php +25 -0
  323. package/dist/packages/crucible/seeds/php/seed-046.php +20 -0
  324. package/dist/packages/crucible/seeds/php/seed-047.php +21 -0
  325. package/dist/packages/crucible/seeds/php/seed-048.php +19 -0
  326. package/dist/packages/crucible/seeds/php/seed-049.php +18 -0
  327. package/dist/packages/crucible/seeds/php/seed-050.php +20 -0
  328. package/dist/packages/crucible/seeds/python/seed-001.py +33 -0
  329. package/dist/packages/crucible/seeds/python/seed-002.py +39 -0
  330. package/dist/packages/crucible/seeds/python/seed-003.py +31 -0
  331. package/dist/packages/crucible/seeds/python/seed-004.py +33 -0
  332. package/dist/packages/crucible/seeds/python/seed-005.py +33 -0
  333. package/dist/packages/crucible/seeds/python/seed-006.py +34 -0
  334. package/dist/packages/crucible/seeds/python/seed-007.py +33 -0
  335. package/dist/packages/crucible/seeds/python/seed-008.py +38 -0
  336. package/dist/packages/crucible/seeds/python/seed-009.py +38 -0
  337. package/dist/packages/crucible/seeds/python/seed-010.py +42 -0
  338. package/dist/packages/crucible/seeds/python/seed-011.py +30 -0
  339. package/dist/packages/crucible/seeds/python/seed-012.py +35 -0
  340. package/dist/packages/crucible/seeds/python/seed-013.py +33 -0
  341. package/dist/packages/crucible/seeds/python/seed-014.py +40 -0
  342. package/dist/packages/crucible/seeds/python/seed-015.py +33 -0
  343. package/dist/packages/crucible/seeds/python/seed-016.py +34 -0
  344. package/dist/packages/crucible/seeds/python/seed-017.py +34 -0
  345. package/dist/packages/crucible/seeds/python/seed-018.py +30 -0
  346. package/dist/packages/crucible/seeds/python/seed-019.py +33 -0
  347. package/dist/packages/crucible/seeds/python/seed-020.py +37 -0
  348. package/dist/packages/crucible/seeds/python/seed-021 2.py +37 -0
  349. package/dist/packages/crucible/seeds/python/seed-021 3.py +37 -0
  350. package/dist/packages/crucible/seeds/python/seed-021.py +37 -0
  351. package/dist/packages/crucible/seeds/python/seed-022 2.py +34 -0
  352. package/dist/packages/crucible/seeds/python/seed-022 3.py +34 -0
  353. package/dist/packages/crucible/seeds/python/seed-022.py +34 -0
  354. package/dist/packages/crucible/seeds/python/seed-023 2.py +37 -0
  355. package/dist/packages/crucible/seeds/python/seed-023 3.py +37 -0
  356. package/dist/packages/crucible/seeds/python/seed-023.py +37 -0
  357. package/dist/packages/crucible/seeds/python/seed-024 2.py +38 -0
  358. package/dist/packages/crucible/seeds/python/seed-024 3.py +38 -0
  359. package/dist/packages/crucible/seeds/python/seed-024.py +38 -0
  360. package/dist/packages/crucible/seeds/python/seed-025 2.py +40 -0
  361. package/dist/packages/crucible/seeds/python/seed-025 3.py +40 -0
  362. package/dist/packages/crucible/seeds/python/seed-025.py +40 -0
  363. package/dist/packages/crucible/seeds/python/seed-026 2.py +35 -0
  364. package/dist/packages/crucible/seeds/python/seed-026 3.py +35 -0
  365. package/dist/packages/crucible/seeds/python/seed-026.py +35 -0
  366. package/dist/packages/crucible/seeds/python/seed-027 2.py +35 -0
  367. package/dist/packages/crucible/seeds/python/seed-027 3.py +35 -0
  368. package/dist/packages/crucible/seeds/python/seed-027.py +35 -0
  369. package/dist/packages/crucible/seeds/python/seed-028 2.py +42 -0
  370. package/dist/packages/crucible/seeds/python/seed-028 3.py +42 -0
  371. package/dist/packages/crucible/seeds/python/seed-028.py +42 -0
  372. package/dist/packages/crucible/seeds/python/seed-029.py +42 -0
  373. package/dist/packages/crucible/seeds/python/seed-030 2.py +37 -0
  374. package/dist/packages/crucible/seeds/python/seed-030 3.py +37 -0
  375. package/dist/packages/crucible/seeds/python/seed-030.py +37 -0
  376. package/dist/packages/crucible/seeds/python/seed-031 2.py +34 -0
  377. package/dist/packages/crucible/seeds/python/seed-031 3.py +34 -0
  378. package/dist/packages/crucible/seeds/python/seed-031.py +34 -0
  379. package/dist/packages/crucible/seeds/python/seed-032.py +33 -0
  380. package/dist/packages/crucible/seeds/python/seed-033.py +32 -0
  381. package/dist/packages/crucible/seeds/python/seed-034.py +38 -0
  382. package/dist/packages/crucible/seeds/python/seed-035.py +35 -0
  383. package/dist/packages/crucible/seeds/python/seed-036 2.py +33 -0
  384. package/dist/packages/crucible/seeds/python/seed-036 3.py +33 -0
  385. package/dist/packages/crucible/seeds/python/seed-036.py +33 -0
  386. package/dist/packages/crucible/seeds/python/seed-037 2.py +41 -0
  387. package/dist/packages/crucible/seeds/python/seed-037 3.py +41 -0
  388. package/dist/packages/crucible/seeds/python/seed-037.py +41 -0
  389. package/dist/packages/crucible/seeds/python/seed-038 2.py +33 -0
  390. package/dist/packages/crucible/seeds/python/seed-038 3.py +33 -0
  391. package/dist/packages/crucible/seeds/python/seed-038.py +33 -0
  392. package/dist/packages/crucible/seeds/python/seed-039 2.py +39 -0
  393. package/dist/packages/crucible/seeds/python/seed-039 3.py +39 -0
  394. package/dist/packages/crucible/seeds/python/seed-039.py +39 -0
  395. package/dist/packages/crucible/seeds/python/seed-040 2.py +39 -0
  396. package/dist/packages/crucible/seeds/python/seed-040 3.py +39 -0
  397. package/dist/packages/crucible/seeds/python/seed-040.py +39 -0
  398. package/dist/packages/crucible/seeds/python/seed-041 2.py +37 -0
  399. package/dist/packages/crucible/seeds/python/seed-041 3.py +37 -0
  400. package/dist/packages/crucible/seeds/python/seed-041.py +37 -0
  401. package/dist/packages/crucible/seeds/python/seed-042 2.py +38 -0
  402. package/dist/packages/crucible/seeds/python/seed-042 3.py +38 -0
  403. package/dist/packages/crucible/seeds/python/seed-042.py +38 -0
  404. package/dist/packages/crucible/seeds/python/seed-043 2.py +32 -0
  405. package/dist/packages/crucible/seeds/python/seed-043 3.py +32 -0
  406. package/dist/packages/crucible/seeds/python/seed-043.py +32 -0
  407. package/dist/packages/crucible/seeds/python/seed-044 2.py +38 -0
  408. package/dist/packages/crucible/seeds/python/seed-044 3.py +38 -0
  409. package/dist/packages/crucible/seeds/python/seed-044.py +38 -0
  410. package/dist/packages/crucible/seeds/python/seed-045 2.py +36 -0
  411. package/dist/packages/crucible/seeds/python/seed-045 3.py +36 -0
  412. package/dist/packages/crucible/seeds/python/seed-045.py +36 -0
  413. package/dist/packages/crucible/seeds/python/seed-046 2.py +33 -0
  414. package/dist/packages/crucible/seeds/python/seed-046 3.py +33 -0
  415. package/dist/packages/crucible/seeds/python/seed-046.py +33 -0
  416. package/dist/packages/crucible/seeds/python/seed-047 2.py +44 -0
  417. package/dist/packages/crucible/seeds/python/seed-047 3.py +44 -0
  418. package/dist/packages/crucible/seeds/python/seed-047.py +44 -0
  419. package/dist/packages/crucible/seeds/python/seed-048 2.py +35 -0
  420. package/dist/packages/crucible/seeds/python/seed-048 3.py +35 -0
  421. package/dist/packages/crucible/seeds/python/seed-048.py +35 -0
  422. package/dist/packages/crucible/seeds/python/seed-049 2.py +39 -0
  423. package/dist/packages/crucible/seeds/python/seed-049 3.py +39 -0
  424. package/dist/packages/crucible/seeds/python/seed-049.py +39 -0
  425. package/dist/packages/crucible/seeds/python/seed-050 2.py +39 -0
  426. package/dist/packages/crucible/seeds/python/seed-050 3.py +39 -0
  427. package/dist/packages/crucible/seeds/python/seed-050.py +39 -0
  428. package/dist/packages/crucible/seeds/python/seed-051 2.py +38 -0
  429. package/dist/packages/crucible/seeds/python/seed-051 3.py +38 -0
  430. package/dist/packages/crucible/seeds/python/seed-051.py +38 -0
  431. package/dist/packages/crucible/seeds/python/seed-052 2.py +41 -0
  432. package/dist/packages/crucible/seeds/python/seed-052 3.py +41 -0
  433. package/dist/packages/crucible/seeds/python/seed-052.py +41 -0
  434. package/dist/packages/crucible/seeds/ruby/seed-001 2.rb +15 -0
  435. package/dist/packages/crucible/seeds/ruby/seed-001 3.rb +15 -0
  436. package/dist/packages/crucible/seeds/ruby/seed-001.rb +15 -0
  437. package/dist/packages/crucible/seeds/ruby/seed-002 2.rb +22 -0
  438. package/dist/packages/crucible/seeds/ruby/seed-002 3.rb +22 -0
  439. package/dist/packages/crucible/seeds/ruby/seed-002.rb +22 -0
  440. package/dist/packages/crucible/seeds/ruby/seed-003 2.rb +25 -0
  441. package/dist/packages/crucible/seeds/ruby/seed-003 3.rb +25 -0
  442. package/dist/packages/crucible/seeds/ruby/seed-003.rb +25 -0
  443. package/dist/packages/crucible/seeds/ruby/seed-004 2.rb +17 -0
  444. package/dist/packages/crucible/seeds/ruby/seed-004 3.rb +17 -0
  445. package/dist/packages/crucible/seeds/ruby/seed-004.rb +17 -0
  446. package/dist/packages/crucible/seeds/ruby/seed-005 2.rb +21 -0
  447. package/dist/packages/crucible/seeds/ruby/seed-005 3.rb +21 -0
  448. package/dist/packages/crucible/seeds/ruby/seed-005.rb +21 -0
  449. package/dist/packages/crucible/seeds/ruby/seed-006 2.rb +17 -0
  450. package/dist/packages/crucible/seeds/ruby/seed-006 3.rb +17 -0
  451. package/dist/packages/crucible/seeds/ruby/seed-006.rb +17 -0
  452. package/dist/packages/crucible/seeds/ruby/seed-007 2.rb +16 -0
  453. package/dist/packages/crucible/seeds/ruby/seed-007 3.rb +16 -0
  454. package/dist/packages/crucible/seeds/ruby/seed-007.rb +16 -0
  455. package/dist/packages/crucible/seeds/ruby/seed-008 2.rb +18 -0
  456. package/dist/packages/crucible/seeds/ruby/seed-008 3.rb +18 -0
  457. package/dist/packages/crucible/seeds/ruby/seed-008.rb +18 -0
  458. package/dist/packages/crucible/seeds/ruby/seed-009 2.rb +20 -0
  459. package/dist/packages/crucible/seeds/ruby/seed-009 3.rb +20 -0
  460. package/dist/packages/crucible/seeds/ruby/seed-009.rb +20 -0
  461. package/dist/packages/crucible/seeds/ruby/seed-010 2.rb +24 -0
  462. package/dist/packages/crucible/seeds/ruby/seed-010 3.rb +24 -0
  463. package/dist/packages/crucible/seeds/ruby/seed-010.rb +24 -0
  464. package/dist/packages/crucible/seeds/ruby/seed-011 2.rb +21 -0
  465. package/dist/packages/crucible/seeds/ruby/seed-011 3.rb +21 -0
  466. package/dist/packages/crucible/seeds/ruby/seed-011.rb +21 -0
  467. package/dist/packages/crucible/seeds/ruby/seed-012 2.rb +22 -0
  468. package/dist/packages/crucible/seeds/ruby/seed-012 3.rb +22 -0
  469. package/dist/packages/crucible/seeds/ruby/seed-012.rb +22 -0
  470. package/dist/packages/crucible/seeds/ruby/seed-013 2.rb +21 -0
  471. package/dist/packages/crucible/seeds/ruby/seed-013 3.rb +21 -0
  472. package/dist/packages/crucible/seeds/ruby/seed-013.rb +21 -0
  473. package/dist/packages/crucible/seeds/ruby/seed-014 2.rb +16 -0
  474. package/dist/packages/crucible/seeds/ruby/seed-014 3.rb +16 -0
  475. package/dist/packages/crucible/seeds/ruby/seed-014.rb +16 -0
  476. package/dist/packages/crucible/seeds/ruby/seed-015 2.rb +18 -0
  477. package/dist/packages/crucible/seeds/ruby/seed-015 3.rb +18 -0
  478. package/dist/packages/crucible/seeds/ruby/seed-015.rb +18 -0
  479. package/dist/packages/crucible/seeds/ruby/seed-016 2.rb +17 -0
  480. package/dist/packages/crucible/seeds/ruby/seed-016 3.rb +17 -0
  481. package/dist/packages/crucible/seeds/ruby/seed-016.rb +17 -0
  482. package/dist/packages/crucible/seeds/ruby/seed-017 2.rb +25 -0
  483. package/dist/packages/crucible/seeds/ruby/seed-017 3.rb +25 -0
  484. package/dist/packages/crucible/seeds/ruby/seed-017.rb +25 -0
  485. package/dist/packages/crucible/seeds/ruby/seed-018 2.rb +23 -0
  486. package/dist/packages/crucible/seeds/ruby/seed-018 3.rb +23 -0
  487. package/dist/packages/crucible/seeds/ruby/seed-018.rb +23 -0
  488. package/dist/packages/crucible/seeds/ruby/seed-019 2.rb +20 -0
  489. package/dist/packages/crucible/seeds/ruby/seed-019 3.rb +20 -0
  490. package/dist/packages/crucible/seeds/ruby/seed-019.rb +20 -0
  491. package/dist/packages/crucible/seeds/ruby/seed-020 2.rb +17 -0
  492. package/dist/packages/crucible/seeds/ruby/seed-020 3.rb +17 -0
  493. package/dist/packages/crucible/seeds/ruby/seed-020.rb +17 -0
  494. package/dist/packages/crucible/seeds/ruby/seed-021 2.rb +20 -0
  495. package/dist/packages/crucible/seeds/ruby/seed-021 3.rb +20 -0
  496. package/dist/packages/crucible/seeds/ruby/seed-021.rb +20 -0
  497. package/dist/packages/crucible/seeds/ruby/seed-022 2.rb +21 -0
  498. package/dist/packages/crucible/seeds/ruby/seed-022 3.rb +21 -0
  499. package/dist/packages/crucible/seeds/ruby/seed-022.rb +21 -0
  500. package/dist/packages/crucible/seeds/ruby/seed-023 2.rb +19 -0
  501. package/dist/packages/crucible/seeds/ruby/seed-023 3.rb +19 -0
  502. package/dist/packages/crucible/seeds/ruby/seed-023.rb +19 -0
  503. package/dist/packages/crucible/seeds/ruby/seed-024 2.rb +17 -0
  504. package/dist/packages/crucible/seeds/ruby/seed-024 3.rb +17 -0
  505. package/dist/packages/crucible/seeds/ruby/seed-024.rb +17 -0
  506. package/dist/packages/crucible/seeds/ruby/seed-025 2.rb +17 -0
  507. package/dist/packages/crucible/seeds/ruby/seed-025 3.rb +17 -0
  508. package/dist/packages/crucible/seeds/ruby/seed-025.rb +17 -0
  509. package/dist/packages/crucible/seeds/ruby/seed-026 2.rb +18 -0
  510. package/dist/packages/crucible/seeds/ruby/seed-026 3.rb +18 -0
  511. package/dist/packages/crucible/seeds/ruby/seed-026.rb +18 -0
  512. package/dist/packages/crucible/seeds/ruby/seed-027 2.rb +21 -0
  513. package/dist/packages/crucible/seeds/ruby/seed-027 3.rb +21 -0
  514. package/dist/packages/crucible/seeds/ruby/seed-027.rb +21 -0
  515. package/dist/packages/crucible/seeds/ruby/seed-028 2.rb +22 -0
  516. package/dist/packages/crucible/seeds/ruby/seed-028 3.rb +22 -0
  517. package/dist/packages/crucible/seeds/ruby/seed-028.rb +22 -0
  518. package/dist/packages/crucible/seeds/ruby/seed-029 2.rb +19 -0
  519. package/dist/packages/crucible/seeds/ruby/seed-029 3.rb +19 -0
  520. package/dist/packages/crucible/seeds/ruby/seed-029.rb +19 -0
  521. package/dist/packages/crucible/seeds/ruby/seed-030 2.rb +20 -0
  522. package/dist/packages/crucible/seeds/ruby/seed-030 3.rb +20 -0
  523. package/dist/packages/crucible/seeds/ruby/seed-030.rb +20 -0
  524. package/dist/packages/crucible/seeds/ruby/seed-031 2.rb +17 -0
  525. package/dist/packages/crucible/seeds/ruby/seed-031 3.rb +17 -0
  526. package/dist/packages/crucible/seeds/ruby/seed-031.rb +17 -0
  527. package/dist/packages/crucible/seeds/ruby/seed-032 2.rb +22 -0
  528. package/dist/packages/crucible/seeds/ruby/seed-032 3.rb +22 -0
  529. package/dist/packages/crucible/seeds/ruby/seed-032.rb +22 -0
  530. package/dist/packages/crucible/seeds/ruby/seed-033 2.rb +18 -0
  531. package/dist/packages/crucible/seeds/ruby/seed-033 3.rb +18 -0
  532. package/dist/packages/crucible/seeds/ruby/seed-033.rb +18 -0
  533. package/dist/packages/crucible/seeds/ruby/seed-034 2.rb +19 -0
  534. package/dist/packages/crucible/seeds/ruby/seed-034 3.rb +19 -0
  535. package/dist/packages/crucible/seeds/ruby/seed-034.rb +19 -0
  536. package/dist/packages/crucible/seeds/ruby/seed-035 2.rb +19 -0
  537. package/dist/packages/crucible/seeds/ruby/seed-035 3.rb +19 -0
  538. package/dist/packages/crucible/seeds/ruby/seed-035.rb +19 -0
  539. package/dist/packages/crucible/seeds/ruby/seed-036 2.rb +18 -0
  540. package/dist/packages/crucible/seeds/ruby/seed-036 3.rb +18 -0
  541. package/dist/packages/crucible/seeds/ruby/seed-036.rb +18 -0
  542. package/dist/packages/crucible/seeds/ruby/seed-037 2.rb +21 -0
  543. package/dist/packages/crucible/seeds/ruby/seed-037 3.rb +21 -0
  544. package/dist/packages/crucible/seeds/ruby/seed-037.rb +21 -0
  545. package/dist/packages/crucible/seeds/ruby/seed-038 2.rb +24 -0
  546. package/dist/packages/crucible/seeds/ruby/seed-038 3.rb +24 -0
  547. package/dist/packages/crucible/seeds/ruby/seed-038.rb +24 -0
  548. package/dist/packages/crucible/seeds/ruby/seed-039 2.rb +24 -0
  549. package/dist/packages/crucible/seeds/ruby/seed-039 3.rb +24 -0
  550. package/dist/packages/crucible/seeds/ruby/seed-039.rb +24 -0
  551. package/dist/packages/crucible/seeds/ruby/seed-040 2.rb +22 -0
  552. package/dist/packages/crucible/seeds/ruby/seed-040 3.rb +22 -0
  553. package/dist/packages/crucible/seeds/ruby/seed-040.rb +22 -0
  554. package/dist/packages/crucible/seeds/ruby/seed-041 2.rb +23 -0
  555. package/dist/packages/crucible/seeds/ruby/seed-041 3.rb +23 -0
  556. package/dist/packages/crucible/seeds/ruby/seed-041.rb +23 -0
  557. package/dist/packages/crucible/seeds/ruby/seed-042 2.rb +25 -0
  558. package/dist/packages/crucible/seeds/ruby/seed-042 3.rb +25 -0
  559. package/dist/packages/crucible/seeds/ruby/seed-042.rb +25 -0
  560. package/dist/packages/crucible/seeds/ruby/seed-043 2.rb +23 -0
  561. package/dist/packages/crucible/seeds/ruby/seed-043 3.rb +23 -0
  562. package/dist/packages/crucible/seeds/ruby/seed-043.rb +23 -0
  563. package/dist/packages/crucible/seeds/ruby/seed-044 2.rb +16 -0
  564. package/dist/packages/crucible/seeds/ruby/seed-044 3.rb +16 -0
  565. package/dist/packages/crucible/seeds/ruby/seed-044.rb +16 -0
  566. package/dist/packages/crucible/seeds/ruby/seed-045 2.rb +22 -0
  567. package/dist/packages/crucible/seeds/ruby/seed-045 3.rb +22 -0
  568. package/dist/packages/crucible/seeds/ruby/seed-045.rb +22 -0
  569. package/dist/packages/crucible/seeds/ruby/seed-046 2.rb +27 -0
  570. package/dist/packages/crucible/seeds/ruby/seed-046 3.rb +27 -0
  571. package/dist/packages/crucible/seeds/ruby/seed-046.rb +27 -0
  572. package/dist/packages/crucible/seeds/ruby/seed-047 2.rb +26 -0
  573. package/dist/packages/crucible/seeds/ruby/seed-047 3.rb +26 -0
  574. package/dist/packages/crucible/seeds/ruby/seed-047.rb +26 -0
  575. package/dist/packages/crucible/seeds/ruby/seed-048 2.rb +24 -0
  576. package/dist/packages/crucible/seeds/ruby/seed-048 3.rb +24 -0
  577. package/dist/packages/crucible/seeds/ruby/seed-048.rb +24 -0
  578. package/dist/packages/crucible/seeds/ruby/seed-049 2.rb +20 -0
  579. package/dist/packages/crucible/seeds/ruby/seed-049 3.rb +20 -0
  580. package/dist/packages/crucible/seeds/ruby/seed-049.rb +20 -0
  581. package/dist/packages/crucible/seeds/ruby/seed-050 2.rb +27 -0
  582. package/dist/packages/crucible/seeds/ruby/seed-050 3.rb +27 -0
  583. package/dist/packages/crucible/seeds/ruby/seed-050.rb +27 -0
  584. package/dist/packages/crucible/seeds/rust/seed-001 2.rs +25 -0
  585. package/dist/packages/crucible/seeds/rust/seed-001 3.rs +25 -0
  586. package/dist/packages/crucible/seeds/rust/seed-001.rs +25 -0
  587. package/dist/packages/crucible/seeds/rust/seed-002 2.rs +20 -0
  588. package/dist/packages/crucible/seeds/rust/seed-002 3.rs +20 -0
  589. package/dist/packages/crucible/seeds/rust/seed-002.rs +20 -0
  590. package/dist/packages/crucible/seeds/rust/seed-003 2.rs +23 -0
  591. package/dist/packages/crucible/seeds/rust/seed-003 3.rs +23 -0
  592. package/dist/packages/crucible/seeds/rust/seed-003.rs +23 -0
  593. package/dist/packages/crucible/seeds/rust/seed-004 2.rs +21 -0
  594. package/dist/packages/crucible/seeds/rust/seed-004 3.rs +21 -0
  595. package/dist/packages/crucible/seeds/rust/seed-004.rs +21 -0
  596. package/dist/packages/crucible/seeds/rust/seed-005 2.rs +21 -0
  597. package/dist/packages/crucible/seeds/rust/seed-005 3.rs +21 -0
  598. package/dist/packages/crucible/seeds/rust/seed-005.rs +21 -0
  599. package/dist/packages/crucible/seeds/rust/seed-006 2.rs +24 -0
  600. package/dist/packages/crucible/seeds/rust/seed-006 3.rs +24 -0
  601. package/dist/packages/crucible/seeds/rust/seed-006.rs +24 -0
  602. package/dist/packages/crucible/seeds/rust/seed-007 2.rs +20 -0
  603. package/dist/packages/crucible/seeds/rust/seed-007 3.rs +20 -0
  604. package/dist/packages/crucible/seeds/rust/seed-007.rs +20 -0
  605. package/dist/packages/crucible/seeds/rust/seed-008 2.rs +19 -0
  606. package/dist/packages/crucible/seeds/rust/seed-008 3.rs +19 -0
  607. package/dist/packages/crucible/seeds/rust/seed-008.rs +19 -0
  608. package/dist/packages/crucible/seeds/rust/seed-009 2.rs +28 -0
  609. package/dist/packages/crucible/seeds/rust/seed-009 3.rs +28 -0
  610. package/dist/packages/crucible/seeds/rust/seed-009.rs +28 -0
  611. package/dist/packages/crucible/seeds/rust/seed-010 2.rs +28 -0
  612. package/dist/packages/crucible/seeds/rust/seed-010 3.rs +28 -0
  613. package/dist/packages/crucible/seeds/rust/seed-010.rs +28 -0
  614. package/dist/packages/crucible/seeds/rust/seed-011 2.rs +25 -0
  615. package/dist/packages/crucible/seeds/rust/seed-011 3.rs +25 -0
  616. package/dist/packages/crucible/seeds/rust/seed-011.rs +25 -0
  617. package/dist/packages/crucible/seeds/rust/seed-012 2.rs +31 -0
  618. package/dist/packages/crucible/seeds/rust/seed-012 3.rs +31 -0
  619. package/dist/packages/crucible/seeds/rust/seed-012.rs +31 -0
  620. package/dist/packages/crucible/seeds/rust/seed-013 2.rs +27 -0
  621. package/dist/packages/crucible/seeds/rust/seed-013 3.rs +27 -0
  622. package/dist/packages/crucible/seeds/rust/seed-013.rs +27 -0
  623. package/dist/packages/crucible/seeds/rust/seed-014 2.rs +30 -0
  624. package/dist/packages/crucible/seeds/rust/seed-014 3.rs +30 -0
  625. package/dist/packages/crucible/seeds/rust/seed-014.rs +30 -0
  626. package/dist/packages/crucible/seeds/rust/seed-015 2.rs +33 -0
  627. package/dist/packages/crucible/seeds/rust/seed-015 3.rs +33 -0
  628. package/dist/packages/crucible/seeds/rust/seed-015.rs +33 -0
  629. package/dist/packages/crucible/seeds/rust/seed-016 2.rs +22 -0
  630. package/dist/packages/crucible/seeds/rust/seed-016 3.rs +22 -0
  631. package/dist/packages/crucible/seeds/rust/seed-016.rs +22 -0
  632. package/dist/packages/crucible/seeds/rust/seed-017 2.rs +28 -0
  633. package/dist/packages/crucible/seeds/rust/seed-017 3.rs +28 -0
  634. package/dist/packages/crucible/seeds/rust/seed-017.rs +28 -0
  635. package/dist/packages/crucible/seeds/rust/seed-018 2.rs +21 -0
  636. package/dist/packages/crucible/seeds/rust/seed-018 3.rs +21 -0
  637. package/dist/packages/crucible/seeds/rust/seed-018.rs +21 -0
  638. package/dist/packages/crucible/seeds/rust/seed-019 2.rs +36 -0
  639. package/dist/packages/crucible/seeds/rust/seed-019 3.rs +36 -0
  640. package/dist/packages/crucible/seeds/rust/seed-019.rs +36 -0
  641. package/dist/packages/crucible/seeds/rust/seed-020 2.rs +27 -0
  642. package/dist/packages/crucible/seeds/rust/seed-020 3.rs +27 -0
  643. package/dist/packages/crucible/seeds/rust/seed-020.rs +27 -0
  644. package/dist/packages/crucible/seeds/rust/seed-021 2.rs +26 -0
  645. package/dist/packages/crucible/seeds/rust/seed-021 3.rs +26 -0
  646. package/dist/packages/crucible/seeds/rust/seed-021.rs +26 -0
  647. package/dist/packages/crucible/seeds/rust/seed-022 2.rs +23 -0
  648. package/dist/packages/crucible/seeds/rust/seed-022 3.rs +23 -0
  649. package/dist/packages/crucible/seeds/rust/seed-022.rs +23 -0
  650. package/dist/packages/crucible/seeds/rust/seed-023 2.rs +22 -0
  651. package/dist/packages/crucible/seeds/rust/seed-023 3.rs +22 -0
  652. package/dist/packages/crucible/seeds/rust/seed-023.rs +22 -0
  653. package/dist/packages/crucible/seeds/rust/seed-024 2.rs +24 -0
  654. package/dist/packages/crucible/seeds/rust/seed-024 3.rs +24 -0
  655. package/dist/packages/crucible/seeds/rust/seed-024.rs +24 -0
  656. package/dist/packages/crucible/seeds/rust/seed-025 2.rs +29 -0
  657. package/dist/packages/crucible/seeds/rust/seed-025 3.rs +29 -0
  658. package/dist/packages/crucible/seeds/rust/seed-025.rs +29 -0
  659. package/dist/packages/crucible/seeds/rust/seed-026 2.rs +23 -0
  660. package/dist/packages/crucible/seeds/rust/seed-026 3.rs +23 -0
  661. package/dist/packages/crucible/seeds/rust/seed-026.rs +23 -0
  662. package/dist/packages/crucible/seeds/rust/seed-027 2.rs +24 -0
  663. package/dist/packages/crucible/seeds/rust/seed-027 3.rs +24 -0
  664. package/dist/packages/crucible/seeds/rust/seed-027.rs +24 -0
  665. package/dist/packages/crucible/seeds/rust/seed-028 2.rs +25 -0
  666. package/dist/packages/crucible/seeds/rust/seed-028 3.rs +25 -0
  667. package/dist/packages/crucible/seeds/rust/seed-028.rs +25 -0
  668. package/dist/packages/crucible/seeds/rust/seed-029 2.rs +25 -0
  669. package/dist/packages/crucible/seeds/rust/seed-029 3.rs +25 -0
  670. package/dist/packages/crucible/seeds/rust/seed-029.rs +25 -0
  671. package/dist/packages/crucible/seeds/rust/seed-030 2.rs +30 -0
  672. package/dist/packages/crucible/seeds/rust/seed-030 3.rs +30 -0
  673. package/dist/packages/crucible/seeds/rust/seed-030.rs +30 -0
  674. package/dist/packages/crucible/seeds/rust/seed-031 2.rs +22 -0
  675. package/dist/packages/crucible/seeds/rust/seed-031 3.rs +22 -0
  676. package/dist/packages/crucible/seeds/rust/seed-031.rs +22 -0
  677. package/dist/packages/crucible/seeds/rust/seed-032 2.rs +25 -0
  678. package/dist/packages/crucible/seeds/rust/seed-032 3.rs +25 -0
  679. package/dist/packages/crucible/seeds/rust/seed-032.rs +25 -0
  680. package/dist/packages/crucible/seeds/rust/seed-033 2.rs +25 -0
  681. package/dist/packages/crucible/seeds/rust/seed-033 3.rs +25 -0
  682. package/dist/packages/crucible/seeds/rust/seed-033.rs +25 -0
  683. package/dist/packages/crucible/seeds/rust/seed-034 2.rs +20 -0
  684. package/dist/packages/crucible/seeds/rust/seed-034 3.rs +20 -0
  685. package/dist/packages/crucible/seeds/rust/seed-034.rs +20 -0
  686. package/dist/packages/crucible/seeds/rust/seed-035 2.rs +28 -0
  687. package/dist/packages/crucible/seeds/rust/seed-035 3.rs +28 -0
  688. package/dist/packages/crucible/seeds/rust/seed-035.rs +28 -0
  689. package/dist/packages/crucible/seeds/rust/seed-036 2.rs +26 -0
  690. package/dist/packages/crucible/seeds/rust/seed-036 3.rs +26 -0
  691. package/dist/packages/crucible/seeds/rust/seed-036.rs +26 -0
  692. package/dist/packages/crucible/seeds/rust/seed-037 2.rs +31 -0
  693. package/dist/packages/crucible/seeds/rust/seed-037 3.rs +31 -0
  694. package/dist/packages/crucible/seeds/rust/seed-037.rs +31 -0
  695. package/dist/packages/crucible/seeds/rust/seed-038 2.rs +25 -0
  696. package/dist/packages/crucible/seeds/rust/seed-038 3.rs +25 -0
  697. package/dist/packages/crucible/seeds/rust/seed-038.rs +25 -0
  698. package/dist/packages/crucible/seeds/rust/seed-039 2.rs +28 -0
  699. package/dist/packages/crucible/seeds/rust/seed-039 3.rs +28 -0
  700. package/dist/packages/crucible/seeds/rust/seed-039.rs +28 -0
  701. package/dist/packages/crucible/seeds/rust/seed-040 2.rs +27 -0
  702. package/dist/packages/crucible/seeds/rust/seed-040 3.rs +27 -0
  703. package/dist/packages/crucible/seeds/rust/seed-040.rs +27 -0
  704. package/dist/packages/crucible/seeds/rust/seed-041 2.rs +32 -0
  705. package/dist/packages/crucible/seeds/rust/seed-041 3.rs +32 -0
  706. package/dist/packages/crucible/seeds/rust/seed-041.rs +32 -0
  707. package/dist/packages/crucible/seeds/rust/seed-042 2.rs +27 -0
  708. package/dist/packages/crucible/seeds/rust/seed-042 3.rs +27 -0
  709. package/dist/packages/crucible/seeds/rust/seed-042.rs +27 -0
  710. package/dist/packages/crucible/seeds/rust/seed-043 2.rs +29 -0
  711. package/dist/packages/crucible/seeds/rust/seed-043 3.rs +29 -0
  712. package/dist/packages/crucible/seeds/rust/seed-043.rs +29 -0
  713. package/dist/packages/crucible/seeds/rust/seed-044 2.rs +25 -0
  714. package/dist/packages/crucible/seeds/rust/seed-044 3.rs +25 -0
  715. package/dist/packages/crucible/seeds/rust/seed-044.rs +25 -0
  716. package/dist/packages/crucible/seeds/rust/seed-045 2.rs +28 -0
  717. package/dist/packages/crucible/seeds/rust/seed-045 3.rs +28 -0
  718. package/dist/packages/crucible/seeds/rust/seed-045.rs +28 -0
  719. package/dist/packages/crucible/seeds/rust/seed-046 2.rs +25 -0
  720. package/dist/packages/crucible/seeds/rust/seed-046 3.rs +25 -0
  721. package/dist/packages/crucible/seeds/rust/seed-046.rs +25 -0
  722. package/dist/packages/crucible/seeds/rust/seed-047 2.rs +34 -0
  723. package/dist/packages/crucible/seeds/rust/seed-047 3.rs +34 -0
  724. package/dist/packages/crucible/seeds/rust/seed-047.rs +34 -0
  725. package/dist/packages/crucible/seeds/rust/seed-048 2.rs +21 -0
  726. package/dist/packages/crucible/seeds/rust/seed-048 3.rs +21 -0
  727. package/dist/packages/crucible/seeds/rust/seed-048.rs +21 -0
  728. package/dist/packages/crucible/seeds/rust/seed-049 2.rs +26 -0
  729. package/dist/packages/crucible/seeds/rust/seed-049 3.rs +26 -0
  730. package/dist/packages/crucible/seeds/rust/seed-049.rs +26 -0
  731. package/dist/packages/crucible/seeds/rust/seed-050 2.rs +23 -0
  732. package/dist/packages/crucible/seeds/rust/seed-050 3.rs +23 -0
  733. package/dist/packages/crucible/seeds/rust/seed-050.rs +23 -0
  734. package/dist/packages/crucible/seeds/ts/seed-001 2.ts +32 -0
  735. package/dist/packages/crucible/seeds/ts/seed-001 3.ts +32 -0
  736. package/dist/packages/crucible/seeds/ts/seed-001.ts +32 -0
  737. package/dist/packages/crucible/seeds/ts/seed-002 2.ts +34 -0
  738. package/dist/packages/crucible/seeds/ts/seed-002 3.ts +34 -0
  739. package/dist/packages/crucible/seeds/ts/seed-002.ts +34 -0
  740. package/dist/packages/crucible/seeds/ts/seed-003 2.ts +28 -0
  741. package/dist/packages/crucible/seeds/ts/seed-003 3.ts +28 -0
  742. package/dist/packages/crucible/seeds/ts/seed-003.ts +28 -0
  743. package/dist/packages/crucible/seeds/ts/seed-004 2.ts +34 -0
  744. package/dist/packages/crucible/seeds/ts/seed-004 3.ts +34 -0
  745. package/dist/packages/crucible/seeds/ts/seed-004.ts +34 -0
  746. package/dist/packages/crucible/seeds/ts/seed-005 2.ts +32 -0
  747. package/dist/packages/crucible/seeds/ts/seed-005 3.ts +32 -0
  748. package/dist/packages/crucible/seeds/ts/seed-005.ts +32 -0
  749. package/dist/packages/crucible/seeds/ts/seed-006 2.ts +31 -0
  750. package/dist/packages/crucible/seeds/ts/seed-006 3.ts +31 -0
  751. package/dist/packages/crucible/seeds/ts/seed-006.ts +31 -0
  752. package/dist/packages/crucible/seeds/ts/seed-007 2.ts +28 -0
  753. package/dist/packages/crucible/seeds/ts/seed-007 3.ts +28 -0
  754. package/dist/packages/crucible/seeds/ts/seed-007.ts +28 -0
  755. package/dist/packages/crucible/seeds/ts/seed-008 2.ts +40 -0
  756. package/dist/packages/crucible/seeds/ts/seed-008 3.ts +40 -0
  757. package/dist/packages/crucible/seeds/ts/seed-008.ts +40 -0
  758. package/dist/packages/crucible/seeds/ts/seed-009 2.ts +31 -0
  759. package/dist/packages/crucible/seeds/ts/seed-009 3.ts +31 -0
  760. package/dist/packages/crucible/seeds/ts/seed-009.ts +31 -0
  761. package/dist/packages/crucible/seeds/ts/seed-010 2.ts +33 -0
  762. package/dist/packages/crucible/seeds/ts/seed-010 3.ts +33 -0
  763. package/dist/packages/crucible/seeds/ts/seed-010.ts +33 -0
  764. package/dist/packages/crucible/seeds/ts/seed-011 2.ts +29 -0
  765. package/dist/packages/crucible/seeds/ts/seed-011 3.ts +29 -0
  766. package/dist/packages/crucible/seeds/ts/seed-011.ts +29 -0
  767. package/dist/packages/crucible/seeds/ts/seed-012 2.ts +34 -0
  768. package/dist/packages/crucible/seeds/ts/seed-012 3.ts +34 -0
  769. package/dist/packages/crucible/seeds/ts/seed-012.ts +34 -0
  770. package/dist/packages/crucible/seeds/ts/seed-013 2.ts +31 -0
  771. package/dist/packages/crucible/seeds/ts/seed-013 3.ts +31 -0
  772. package/dist/packages/crucible/seeds/ts/seed-013.ts +31 -0
  773. package/dist/packages/crucible/seeds/ts/seed-014 2.ts +36 -0
  774. package/dist/packages/crucible/seeds/ts/seed-014 3.ts +36 -0
  775. package/dist/packages/crucible/seeds/ts/seed-014.ts +36 -0
  776. package/dist/packages/crucible/seeds/ts/seed-015 2.ts +31 -0
  777. package/dist/packages/crucible/seeds/ts/seed-015 3.ts +31 -0
  778. package/dist/packages/crucible/seeds/ts/seed-015.ts +31 -0
  779. package/dist/packages/crucible/seeds/ts/seed-016 2.ts +37 -0
  780. package/dist/packages/crucible/seeds/ts/seed-016 3.ts +37 -0
  781. package/dist/packages/crucible/seeds/ts/seed-016.ts +37 -0
  782. package/dist/packages/crucible/seeds/ts/seed-017 2.ts +44 -0
  783. package/dist/packages/crucible/seeds/ts/seed-017 3.ts +44 -0
  784. package/dist/packages/crucible/seeds/ts/seed-017.ts +44 -0
  785. package/dist/packages/crucible/seeds/ts/seed-018 2.ts +33 -0
  786. package/dist/packages/crucible/seeds/ts/seed-018 3.ts +33 -0
  787. package/dist/packages/crucible/seeds/ts/seed-018.ts +33 -0
  788. package/dist/packages/crucible/seeds/ts/seed-019 2.ts +32 -0
  789. package/dist/packages/crucible/seeds/ts/seed-019 3.ts +32 -0
  790. package/dist/packages/crucible/seeds/ts/seed-019.ts +32 -0
  791. package/dist/packages/crucible/seeds/ts/seed-020 2.ts +33 -0
  792. package/dist/packages/crucible/seeds/ts/seed-020 3.ts +33 -0
  793. package/dist/packages/crucible/seeds/ts/seed-020.ts +33 -0
  794. package/dist/packages/crucible/seeds/ts/seed-021 2.ts +33 -0
  795. package/dist/packages/crucible/seeds/ts/seed-021 3.ts +33 -0
  796. package/dist/packages/crucible/seeds/ts/seed-021.ts +33 -0
  797. package/dist/packages/crucible/seeds/ts/seed-022 2.ts +34 -0
  798. package/dist/packages/crucible/seeds/ts/seed-022 3.ts +34 -0
  799. package/dist/packages/crucible/seeds/ts/seed-022.ts +34 -0
  800. package/dist/packages/crucible/seeds/ts/seed-023 2.ts +33 -0
  801. package/dist/packages/crucible/seeds/ts/seed-023 3.ts +33 -0
  802. package/dist/packages/crucible/seeds/ts/seed-023.ts +33 -0
  803. package/dist/packages/crucible/seeds/ts/seed-024 2.ts +35 -0
  804. package/dist/packages/crucible/seeds/ts/seed-024 3.ts +35 -0
  805. package/dist/packages/crucible/seeds/ts/seed-024.ts +35 -0
  806. package/dist/packages/crucible/seeds/ts/seed-025 2.ts +29 -0
  807. package/dist/packages/crucible/seeds/ts/seed-025 3.ts +29 -0
  808. package/dist/packages/crucible/seeds/ts/seed-025.ts +29 -0
  809. package/dist/packages/crucible/seeds/ts/seed-026 2.ts +36 -0
  810. package/dist/packages/crucible/seeds/ts/seed-026 3.ts +36 -0
  811. package/dist/packages/crucible/seeds/ts/seed-026.ts +36 -0
  812. package/dist/packages/crucible/seeds/ts/seed-027 2.ts +30 -0
  813. package/dist/packages/crucible/seeds/ts/seed-027 3.ts +30 -0
  814. package/dist/packages/crucible/seeds/ts/seed-027.ts +30 -0
  815. package/dist/packages/crucible/seeds/ts/seed-028 2.ts +32 -0
  816. package/dist/packages/crucible/seeds/ts/seed-028 3.ts +32 -0
  817. package/dist/packages/crucible/seeds/ts/seed-028.ts +32 -0
  818. package/dist/packages/crucible/seeds/ts/seed-029 2.ts +34 -0
  819. package/dist/packages/crucible/seeds/ts/seed-029 3.ts +34 -0
  820. package/dist/packages/crucible/seeds/ts/seed-029.ts +34 -0
  821. package/dist/packages/crucible/seeds/ts/seed-030 2.ts +37 -0
  822. package/dist/packages/crucible/seeds/ts/seed-030 3.ts +37 -0
  823. package/dist/packages/crucible/seeds/ts/seed-030.ts +37 -0
  824. package/dist/packages/crucible/seeds/ts/seed-031 2.ts +31 -0
  825. package/dist/packages/crucible/seeds/ts/seed-031 3.ts +31 -0
  826. package/dist/packages/crucible/seeds/ts/seed-031.ts +31 -0
  827. package/dist/packages/crucible/seeds/ts/seed-032 2.ts +32 -0
  828. package/dist/packages/crucible/seeds/ts/seed-032 3.ts +32 -0
  829. package/dist/packages/crucible/seeds/ts/seed-032.ts +32 -0
  830. package/dist/packages/crucible/seeds/ts/seed-033 2.ts +32 -0
  831. package/dist/packages/crucible/seeds/ts/seed-033 3.ts +32 -0
  832. package/dist/packages/crucible/seeds/ts/seed-033.ts +32 -0
  833. package/dist/packages/crucible/seeds/ts/seed-034 2.ts +34 -0
  834. package/dist/packages/crucible/seeds/ts/seed-034 3.ts +34 -0
  835. package/dist/packages/crucible/seeds/ts/seed-034.ts +34 -0
  836. package/dist/packages/crucible/seeds/ts/seed-035 2.ts +29 -0
  837. package/dist/packages/crucible/seeds/ts/seed-035 3.ts +29 -0
  838. package/dist/packages/crucible/seeds/ts/seed-035.ts +29 -0
  839. package/dist/packages/crucible/seeds/ts/seed-036 2.ts +34 -0
  840. package/dist/packages/crucible/seeds/ts/seed-036 3.ts +34 -0
  841. package/dist/packages/crucible/seeds/ts/seed-036.ts +34 -0
  842. package/dist/packages/crucible/seeds/ts/seed-037 2.ts +33 -0
  843. package/dist/packages/crucible/seeds/ts/seed-037 3.ts +33 -0
  844. package/dist/packages/crucible/seeds/ts/seed-037.ts +33 -0
  845. package/dist/packages/crucible/seeds/ts/seed-038 2.ts +29 -0
  846. package/dist/packages/crucible/seeds/ts/seed-038 3.ts +29 -0
  847. package/dist/packages/crucible/seeds/ts/seed-038.ts +29 -0
  848. package/dist/packages/crucible/seeds/ts/seed-039 2.ts +35 -0
  849. package/dist/packages/crucible/seeds/ts/seed-039 3.ts +35 -0
  850. package/dist/packages/crucible/seeds/ts/seed-039.ts +35 -0
  851. package/dist/packages/crucible/seeds/ts/seed-040 2.ts +31 -0
  852. package/dist/packages/crucible/seeds/ts/seed-040 3.ts +31 -0
  853. package/dist/packages/crucible/seeds/ts/seed-040.ts +31 -0
  854. package/dist/packages/crucible/seeds/ts/seed-041 2.ts +29 -0
  855. package/dist/packages/crucible/seeds/ts/seed-041 3.ts +29 -0
  856. package/dist/packages/crucible/seeds/ts/seed-041.ts +29 -0
  857. package/dist/packages/crucible/seeds/ts/seed-042 2.ts +33 -0
  858. package/dist/packages/crucible/seeds/ts/seed-042 3.ts +33 -0
  859. package/dist/packages/crucible/seeds/ts/seed-042.ts +33 -0
  860. package/dist/packages/crucible/seeds/ts/seed-043 2.ts +32 -0
  861. package/dist/packages/crucible/seeds/ts/seed-043 3.ts +32 -0
  862. package/dist/packages/crucible/seeds/ts/seed-043.ts +32 -0
  863. package/dist/packages/crucible/seeds/ts/seed-044 2.ts +36 -0
  864. package/dist/packages/crucible/seeds/ts/seed-044 3.ts +36 -0
  865. package/dist/packages/crucible/seeds/ts/seed-044.ts +36 -0
  866. package/dist/packages/crucible/seeds/ts/seed-045 2.ts +39 -0
  867. package/dist/packages/crucible/seeds/ts/seed-045 3.ts +39 -0
  868. package/dist/packages/crucible/seeds/ts/seed-045.ts +39 -0
  869. package/dist/packages/crucible/seeds/ts/seed-046 2.ts +41 -0
  870. package/dist/packages/crucible/seeds/ts/seed-046 3.ts +41 -0
  871. package/dist/packages/crucible/seeds/ts/seed-046.ts +41 -0
  872. package/dist/packages/crucible/seeds/ts/seed-047 2.ts +33 -0
  873. package/dist/packages/crucible/seeds/ts/seed-047 3.ts +33 -0
  874. package/dist/packages/crucible/seeds/ts/seed-047.ts +33 -0
  875. package/dist/packages/crucible/seeds/ts/seed-048 2.ts +33 -0
  876. package/dist/packages/crucible/seeds/ts/seed-048 3.ts +33 -0
  877. package/dist/packages/crucible/seeds/ts/seed-048.ts +33 -0
  878. package/dist/packages/crucible/seeds/ts/seed-049 2.ts +34 -0
  879. package/dist/packages/crucible/seeds/ts/seed-049 3.ts +34 -0
  880. package/dist/packages/crucible/seeds/ts/seed-049.ts +34 -0
  881. package/dist/packages/crucible/seeds/ts/seed-050 2.ts +41 -0
  882. package/dist/packages/crucible/seeds/ts/seed-050 3.ts +41 -0
  883. package/dist/packages/crucible/seeds/ts/seed-050.ts +41 -0
  884. package/dist/packages/crucible/seeds/ts/seed-051 2.ts +34 -0
  885. package/dist/packages/crucible/seeds/ts/seed-051 3.ts +34 -0
  886. package/dist/packages/crucible/seeds/ts/seed-051.ts +34 -0
  887. package/dist/packages/crucible/seeds/ts/seed-052 2.ts +36 -0
  888. package/dist/packages/crucible/seeds/ts/seed-052 3.ts +36 -0
  889. package/dist/packages/crucible/seeds/ts/seed-052.ts +36 -0
  890. package/dist/packages/crucible/self-attack.js +1 -0
  891. package/dist/packages/scanner/ai-confidence-scorer.js +1 -1
  892. package/dist/packages/scanner/alert-manager.js +1 -1
  893. package/dist/packages/scanner/ast-analyzer.js +1 -0
  894. package/dist/packages/scanner/baseline-manager.js +1 -1
  895. package/dist/packages/scanner/cfg-analyzer.js +1 -0
  896. package/dist/packages/scanner/code-scanner.js +1 -1
  897. package/dist/packages/scanner/dependency-graph.js +1 -1
  898. package/dist/packages/scanner/drift-detector.js +1 -1
  899. package/dist/packages/scanner/executive-report.js +1 -1
  900. package/dist/packages/scanner/export-verifier.js +1 -0
  901. package/dist/packages/scanner/file-collector.js +1 -1
  902. package/dist/packages/scanner/file-watcher.js +1 -1
  903. package/dist/packages/scanner/ghost-code-detector.js +1 -1
  904. package/dist/packages/scanner/go-ast-analyzer.js +1 -0
  905. package/dist/packages/scanner/hallucination-detector.js +1 -1
  906. package/dist/packages/scanner/html-report.js +1 -1
  907. package/dist/packages/scanner/license-manager.js +1 -1
  908. package/dist/packages/scanner/master-health-checker.js +1 -1
  909. package/dist/packages/scanner/monitor-service.js +1 -1
  910. package/dist/packages/scanner/monitor-store.js +1 -1
  911. package/dist/packages/scanner/python-ast-analyzer.js +1 -0
  912. package/dist/packages/scanner/secret-scanner.js +1 -1
  913. package/dist/packages/scanner/sentinel-core.js +1 -1
  914. package/dist/packages/scanner/taint-tracker.js +1 -0
  915. package/dist/packages/scanner/tech-debt-analyzer.js +1 -1
  916. package/dist/packages/scanner/tech-debt-cost.js +1 -1
  917. package/dist/packages/scanner/widget-generator.js +1 -1
  918. package/package.json +4 -1
@@ -0,0 +1,41 @@
1
+ // [CRUCIBLE-SEED]
2
+ // category: ssrf
3
+ // severity: high
4
+ // language: typescript
5
+ // expected: SSRF via got library with user-controlled URL in oEmbed/link-preview feature
6
+
7
+ import got from 'got';
8
+ import { Request, Response, Router } from 'express';
9
+
10
+ const router = Router();
11
+
12
+ interface LinkPreviewResult {
13
+ title: string;
14
+ description: string;
15
+ image?: string;
16
+ url: string;
17
+ }
18
+
19
+ router.get('/link-preview', async (req: Request, res: Response) => {
20
+ const url = req.query.url as string;
21
+
22
+ // url is user-controlled — attacker can reach internal services
23
+ const response = await got(url, {
24
+ followRedirect: true,
25
+ timeout: { request: 8000 },
26
+ throwHttpErrors: false,
27
+ });
28
+
29
+ const titleMatch = response.body.match(/<title>(.*?)<\/title>/i);
30
+ const descMatch = response.body.match(/<meta name="description" content="(.*?)"/i);
31
+
32
+ const result: LinkPreviewResult = {
33
+ title: titleMatch?.[1] ?? '',
34
+ description: descMatch?.[1] ?? '',
35
+ url,
36
+ };
37
+
38
+ res.json(result);
39
+ });
40
+
41
+ export default router;
@@ -0,0 +1,41 @@
1
+ // [CRUCIBLE-SEED]
2
+ // category: ssrf
3
+ // severity: high
4
+ // language: typescript
5
+ // expected: SSRF via got library with user-controlled URL in oEmbed/link-preview feature
6
+
7
+ import got from 'got';
8
+ import { Request, Response, Router } from 'express';
9
+
10
+ const router = Router();
11
+
12
+ interface LinkPreviewResult {
13
+ title: string;
14
+ description: string;
15
+ image?: string;
16
+ url: string;
17
+ }
18
+
19
+ router.get('/link-preview', async (req: Request, res: Response) => {
20
+ const url = req.query.url as string;
21
+
22
+ // url is user-controlled — attacker can reach internal services
23
+ const response = await got(url, {
24
+ followRedirect: true,
25
+ timeout: { request: 8000 },
26
+ throwHttpErrors: false,
27
+ });
28
+
29
+ const titleMatch = response.body.match(/<title>(.*?)<\/title>/i);
30
+ const descMatch = response.body.match(/<meta name="description" content="(.*?)"/i);
31
+
32
+ const result: LinkPreviewResult = {
33
+ title: titleMatch?.[1] ?? '',
34
+ description: descMatch?.[1] ?? '',
35
+ url,
36
+ };
37
+
38
+ res.json(result);
39
+ });
40
+
41
+ export default router;
@@ -0,0 +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;
@@ -0,0 +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;
@@ -0,0 +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;
@@ -0,0 +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;
@@ -0,0 +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;
@@ -0,0 +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;
@@ -0,0 +1 @@
1
+ "use strict";const fs=require("fs"),path=require("path"),os=require("os"),crypto=require("crypto"),{execFileSync:execFileSync,spawnSync:spawnSync}=require("child_process"),PAYLOADS=[{name:"megaLine",description:"100KB+ file with all code on a single line (no newlines)",filename:"mega-line.js",attackGoal:"crash",expectedIssues:["hardcoded_secret"],get content(){return("const API_KEY = 'sk-REAL1234567890abcdef1234567890'; "+"const x = 1 + 1; ".repeat(5500)).replace(/\n/g," ")}},{name:"binaryAsJs",description:"Binary data with .js extension (should not crash, should skip gracefully)",filename:"binary-disguise.js",attackGoal:"crash",expectedIssues:[],get content(){const e=Buffer.alloc(4096);for(let t=0;t<e.length;t++)e[t]=(137*t+42)%256;return Buffer.from("// js file\nconst x = 1;\n").copy(e),e}},{name:"deeplyNested",description:"20 levels of deeply nested function calls and objects",filename:"deeply-nested.js",attackGoal:"crash",expectedIssues:["eval_abuse"],get content(){let e="eval(data)";for(let t=0;t<20;t++)e=`function level${t}() { return ${e}; } level${t}();`;return`'use strict';\nconst db_password = 'hunter2'; \n${e}`}},{name:"unicodeHomoglyphs",description:"Uses Cyrillic/Greek homoglyphs to hide variable names and strings",filename:"unicode-homoglyphs.js",attackGoal:"false_negative",expectedIssues:["hardcoded_secret"],get content(){return"\n// [CRUCIBLE-SELF-ATTACK] unicode homoglyphs\nconst АPI_КEY = 'sk-t3st1234567890abcdef'; // Cyrillic А, К\nconst pаsswоrd = 'supers3cret!'; // Cyrillic а, о\nconst dаtаbаse_pаss = 'hunter2'; // mixed\nmodule.exports = { АPI_КEY, pаsswоrd };\n"}},{name:"zeroWidthChars",description:"Zero-width spaces (U+200B) injected into keyword names",filename:"zero-width.js",attackGoal:"false_negative",expectedIssues:[],get content(){return"\n// [CRUCIBLE-SELF-ATTACK] zero-width char injection\nconst fn = e​v​a​l;\nfn(userInput); // should this be detected?\nconst pass​word = 'myS3cretP@ss';\n"}},{name:"rtlOverride",description:"Unicode RTL override (CVE-2021-42574 Trojan Source attack)",filename:"trojan-source.js",attackGoal:"false_negative",expectedIssues:["hardcoded_secret"],get content(){return"\n// [CRUCIBLE-SELF-ATTACK] Trojan Source (RTL override)\nif (access_level != 'user‮ ⁦// Check if admin⁩ ⁦') {\n // This string contains RTL override characters\n}\nconst SECRET_KEY = 'AKIAIOSFODNN7EXAMPLE';\nconst password = 'hunter2trojan';\n"}},{name:"extremelyLongLines",description:"File with 5 lines, each 10,000+ characters long",filename:"long-lines.js",attackGoal:"crash",expectedIssues:["hardcoded_secret"],get content(){const e=[];for(let t=0;t<5;t++){const s=`const x${t} = `.padEnd(100," ")+'"'+"a".repeat(9800)+'";';e.push(s)}return e.push("const API_SECRET = 'ghp_realtoken123456789012345678901234';"),e.join("\n")}},{name:"nullBytes",description:"File containing null bytes interspersed in code",filename:"null-bytes.js",attackGoal:"crash",expectedIssues:[],get content(){let e="";for(let t=0;t<40;t++)e+='const x = 1;\nconst password = "secret";\n'[t],t%20==19&&(e+="\0");return e}},{name:"emptyFile",description:"Completely empty file with .js extension",filename:"empty.js",attackGoal:"crash",expectedIssues:[],content:""},{name:"onlyComments",description:"File with only multi-line comments and no code",filename:"only-comments.js",attackGoal:"crash",expectedIssues:[],get content(){return Array(200).fill("").map((e,t)=>`// This is comment line ${t} — nothing to see here. The password is definitely not hunter2.`).join("\n")}},{name:"deeplyNestedJson",description:"50-levels deep nested object literal in JS",filename:"deep-json.js",attackGoal:"crash",expectedIssues:["hardcoded_secret"],get content(){let e='{ "password": "SuperSecret123!", "db": "production" }';for(let t=0;t<50;t++)e=`{ "level${t}": ${e} }`;return`const config = ${e};\nmodule.exports = config;`}},{name:"circularRequire",description:"File that tries to require itself (circular dependency)",filename:"circular-self.js",attackGoal:"crash",expectedIssues:[],content:"\n// [CRUCIBLE-SELF-ATTACK] circular require\nconst self = require('./circular-self');\nconst crypto = require('crypto');\nmodule.exports = { self, hash: crypto.createHash('md5').update('password').digest('hex') };\n"},{name:"giantRepetitive",description:"500KB file with thousands of repetitive variable declarations",filename:"giant-file.js",attackGoal:"crash",expectedIssues:["sql_injection"],get content(){return"const password = 'admin123';\nconst q = \"SELECT * FROM users WHERE id = \" + req.params.id;\n"+Array(9e3).fill("").map((e,t)=>`const var${t} = ${t} * 2 + 1;`).join("\n")}},{name:"mixedDirections",description:"Mix of LTR and RTL Unicode characters in strings",filename:"mixed-directions.js",attackGoal:"false_negative",expectedIssues:["hardcoded_secret"],get content(){const e="مرحبا";return`\n// [CRUCIBLE-SELF-ATTACK] mixed directions\nconst greeting = "${e}";\nconst apiKey = "AKIAIOSFODNN7MIXEDDIR";\nconst SECRET = "sk-${e}secret123";\nmodule.exports = { apiKey, SECRET };\n`}},{name:"jsonProtoPollution",description:"JSON.parse payload with __proto__ pollution",filename:"json-proto-pollution.js",attackGoal:"false_negative",expectedIssues:["prototype_pollution"],content:'\n// [CRUCIBLE-SELF-ATTACK] JSON prototype pollution\nconst maliciousPayload = \'{"__proto__":{"isAdmin":true,"polluted":true}}\';\nconst parsed = JSON.parse(maliciousPayload);\nObject.assign({}, parsed); // spreads __proto__ pollution\nconst target = {};\nconsole.log(target.isAdmin); // true — polluted!\n'},{name:"evalEncodedPayload",description:"eval() with base64-encoded payload to evade pattern matching",filename:"eval-encoded.js",attackGoal:"false_negative",expectedIssues:["eval_abuse"],get content(){return`\n// [CRUCIBLE-SELF-ATTACK] eval with base64 payload\nconst _exec = eval(Buffer.from('${Buffer.from('require("child_process").execSync("id")').toString("base64")}', 'base64').toString());\nmodule.exports = _exec;\n`}},{name:"unicodeEscapes",description:'JavaScript \\uXXXX escape sequences to spell "eval"',filename:"unicode-escapes.js",attackGoal:"false_negative",expectedIssues:["eval_abuse"],content:"\n// [CRUCIBLE-SELF-ATTACK] unicode escape sequences\n// \\u0065\\u0076\\u0061\\u006C spells \"eval\"\nconst fn = \\u0065\\u0076\\u0061\\u006C;\nfn(userControlledInput);\nconst pw = 'hardcoded_password_here';\n"},{name:"taggedTemplateSQLi",description:"Tagged template literal used to build SQL (tagged templates evade simple regex)",filename:"tagged-template-sqli.js",attackGoal:"false_negative",expectedIssues:["sql_injection"],content:"\n// [CRUCIBLE-SELF-ATTACK] tagged template SQL injection\nfunction sql(strings, ...values) {\n // Intentionally unsafe: no parameterization\n return strings.reduce((acc, str, i) => acc + str + (values[i] || ''), '');\n}\n\nconst userId = req.query.id;\nconst query = sql`SELECT * FROM users WHERE id = ${userId}`;\ndb.execute(query);\n"},{name:"polyglotJsHtml",description:"Valid as both JavaScript and HTML (polyglot file)",filename:"polyglot.js",attackGoal:"crash",expectedIssues:["xss"],content:"\x3c!--\nconst secret = 'hunter2';\ndocument.write('<script>alert(' + location.search + ')<\/script>');\n--\x3e\n<script>\nconst password = 'admin123';\ndocument.innerHTML = location.hash;\n<\/script>\n"},{name:"repeatedLines",description:"10,000 copies of the same vulnerable line",filename:"repeated-vuln.js",attackGoal:"crash",expectedIssues:["sql_injection"],get content(){return Array(1e4).fill('const q = "SELECT * FROM users WHERE id = " + userId;').join("\n")}}];class SelfAttack{constructor(e={}){this.thubancli=e.thubancli||this._detectCLI(),this.timeout=e.timeout||3e4,this.level=e.level||"gentle",this._sessionDir=null}_detectCLI(){const e=[path.join(__dirname,"../../cli.js"),path.join(__dirname,"../../dist/cli.js"),path.join(__dirname,"../../bin/thuban.js")];for(const t of e)if(fs.existsSync(t))return t;return null}_createSession(){const e=crypto.randomUUID?crypto.randomUUID():crypto.randomBytes(16).toString("hex");return this._sessionDir=path.join(os.tmpdir(),`thuban-crucible-${e}`),fs.mkdirSync(this._sessionDir,{recursive:!0}),this._sessionDir}_cleanup(){if(this._sessionDir&&fs.existsSync(this._sessionDir)){try{fs.rmSync(this._sessionDir,{recursive:!0,force:!0})}catch{}this._sessionDir=null}}_writePayload(e,t){const s=path.join(e,t.filename),n=t.content instanceof Buffer?t.content:String(t.content||"");return n instanceof Buffer?fs.writeFileSync(s,n):fs.writeFileSync(s,n,"utf-8"),s}_simulateScan(e){let t="";try{const s=fs.readFileSync(e);t=s.toString("utf-8")}catch{return{crashed:!1,timedOut:!1,exitCode:0,output:"{}",error:"",skipped:!1}}const s=[{regex:/AKIA[0-9A-Z]{16}|sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|AIzaSy[a-zA-Z0-9]{33}/i,category:"hardcoded_secret"},{regex:/['"`]\s*\+\s*\w+.*(?:SELECT|INSERT|UPDATE|DELETE)|(?:SELECT|INSERT|UPDATE|DELETE).*\+\s*\w+|"SELECT.*"\s*\+/i,category:"sql_injection"},{regex:/eval\s*\(|new\s+Function\s*\(/,category:"eval_abuse"},{regex:/innerHTML\s*=|document\.write\s*\(/,category:"xss"},{regex:/__proto__|["']__proto__["']/,category:"prototype_pollution"}].filter(e=>{try{return e.regex.test(t)}catch{return!1}}).map(e=>e.category);if(0===s.length)return{crashed:!1,timedOut:!1,exitCode:0,output:"{}",error:"",skipped:!1};const n=s.map(e=>({category:e}));return{crashed:!1,timedOut:!1,exitCode:1,output:JSON.stringify({issues:n}),error:"",skipped:!1}}_runScan(e){if(!this.thubancli)return this._simulateScan(e);try{const t=spawnSync(process.execPath,[this.thubancli,"scan",e,"--json"],{timeout:this.timeout,maxBuffer:10485760,encoding:"utf-8"});return{crashed:null!==t.signal&&void 0!==t.signal,timedOut:"SIGTERM"===t.signal||"ETIMEDOUT"===t.error?.code,exitCode:t.status||0,output:t.stdout||"",error:t.stderr||"",skipped:!1}}catch(e){return{crashed:!0,timedOut:!1,exitCode:-1,output:"",error:e.message,skipped:!1}}}_parseDetections(e){const t=new Set;try{const s=JSON.parse(e);for(const e of s.issues||[])e.category&&t.add(e.category),e.id&&t.add(e.id),e.type&&t.add(e.type)}catch{}return t}async run(e={}){const t=this._createSession(),s={timestamp:(new Date).toISOString(),payloadCount:0,crashes:[],hangs:[],missedDetections:[],passed:[],summary:""},n=e.only?PAYLOADS.filter(t=>e.only.includes(t.name)):PAYLOADS;try{for(const o of n){let n,r;s.payloadCount++;try{n=this._writePayload(t,o),r=this._runScan(n)}catch(t){const n={payload:o.name,description:o.description,error:t.message};s.crashes.push(n),e.onProgress&&e.onProgress(o,{type:"crash",...n});continue}if(r.crashed&&!r.timedOut){const t={payload:o.name,description:o.description,exitCode:r.exitCode,error:r.error};s.crashes.push(t),e.onProgress&&e.onProgress(o,{type:"crash",...t})}else if(r.timedOut){const t={payload:o.name,description:o.description,timeout:this.timeout};s.hangs.push(t),e.onProgress&&e.onProgress(o,{type:"hang",...t})}else{const t=this._parseDetections(r.output),n=o.expectedIssues.filter(e=>!t.has(e));if(n.length>0&&!r.skipped){const r={payload:o.name,description:o.description,missed:n,detected:Array.from(t)};s.missedDetections.push(r),e.onProgress&&e.onProgress(o,{type:"missed",...r})}else{const t={payload:o.name,description:o.description};s.passed.push(t),e.onProgress&&e.onProgress(o,{type:"pass",...t})}}}}finally{this._cleanup()}const o=s.payloadCount,r=s.crashes.length+s.hangs.length,a=s.missedDetections.length;return s.score=Math.max(0,Math.round((o-r-a)/o*100)),s.summary=0===r&&0===a?`All ${o} self-attack payloads handled gracefully`:`${r} crash/hang(s), ${a} missed detection(s) across ${o} payloads`,s}static listPayloads(){return PAYLOADS.map(e=>({name:e.name,description:e.description,attackGoal:e.attackGoal,filename:e.filename}))}static get payloadCount(){return PAYLOADS.length}}module.exports={SelfAttack:SelfAttack,PAYLOADS:PAYLOADS};
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),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()}scan(e){const t=[];for(const s of e){let e;try{if(fs.statSync(s).size>524288)continue;e=fs.readFileSync(s,"utf-8")}catch(e){continue}const n=path.relative(this.rootPath,s);if(this._isSkippable(n))continue;const r=this._extractFunctions(e,n);t.push(...r)}t.sort((e,t)=>t.confidence-e.confidence);const s=t.filter(e=>e.confidence>=70),n=t.filter(e=>e.confidence>=40&&e.confidence<70);return{functions:t,highConfidence:s,mediumConfidence:n,totalFunctions:t.length,aiLikelyCount:s.length,aiPossibleCount:n.length,aiPercentage:t.length>0?Math.round(s.length/t.length*100):0}}_extractFunctions(e,t){const s=e.split("\n"),n=[];for(let e=0;e<s.length;e++){const r=this._detectFunction(s,e);if(!r)continue;const o=this._findFunctionEnd(s,e),i=s.slice(e,o+1).join("\n"),c=s.slice(e,o+1),a=this._scoreFunction(r.name,i,c,s,e);n.push({file:t,name:r.name,line:e+1,lineCount:o-e+1,confidence:a.score,signals:a.reasons,testCoverage:this._hasTests(r.name,t)?"found":"none",category:"ai_confidence",severity:a.score>=80?"high":a.score>=50?"medium":"low"}),e=o}return n}_scoreFunction(e,t,s,n,r){let o=0;const i=[],c=AI_SIGNALS.genericNames.filter(e=>{const s=new RegExp(`\\b${e}\\b`,"g");return(t.match(s)||[]).length>=2}).length;c>=3?(o+=15,i.push(`${c} generic variable names (data, result, response...)`)):c>=1&&(o+=8,i.push(`${c} generic variable name(s)`));const a=AI_SIGNALS.aiCommentPatterns.filter(e=>e.test(t)).length;if(a>=2?(o+=20,i.push(`${a} AI-style comments (TODO: implement, handle error, etc.)`)):a>=1&&(o+=10,i.push("AI-style comment detected")),r>0){const e=n.slice(Math.max(0,r-10),r).join("\n");/\/\*\*[\s\S]*@param[\s\S]*@returns?[\s\S]*@(?:throws|example)/s.test(e)&&(o+=10,i.push("Exhaustive JSDoc (params + returns + throws/example)"))}(t.match(/try\s*\{/g)||[]).length>=2&&(o+=10,i.push("Multiple try/catch blocks (AI over-handles errors)"));const l=s.length;return l>=50?(o+=15,i.push(`${l} lines — large self-contained function`)):l>=25&&(o+=8,i.push(`${l} lines`)),0===n.slice(0,r).concat(n.slice(r+l)).filter(t=>t.includes(e)).length&&l>10&&(o+=10,i.push("Isolated — no internal references")),/catch\s*\([^)]*\)\s*\{[^}]*console\.(log|error)\s*\(/.test(t)&&(o+=8,i.push("console.log/error in catch block (AI default pattern)")),/=>\s*\{[\s\n\r]*const\s*\{/.test(t)&&(o+=5,i.push("Immediate destructuring after arrow (common AI pattern)")),(t.match(/`[^`]*\$\{/g)||[]).length>=4&&(o+=5,i.push("Heavy template literal usage")),o=Math.min(100,o),{score:o,reasons:i}}_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 r=t;r<e.length&&r<t+300;r++){for(const t of e[r])"{"===t&&(s++,n=!0),"}"===t&&s--;if(n&&s<=0)return r}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")}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 +1 @@
1
- const fs=require("fs"),path=require("path"),EventEmitter=require("events");class AlertManager extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),alertsDir:e.alertsDir||path.join(e.rootPath||process.cwd(),"sentinel","alerts"),reportsDir:e.reportsDir||path.join(e.rootPath||process.cwd(),"sentinel","reports"),consoleOutput:!1!==e.consoleOutput,fileOutput:!1!==e.fileOutput,maxAlertsPerFile:e.maxAlertsPerFile||1e3,slackWebhook:e.slackWebhook||null,emailConfig:e.emailConfig||null,...e},this._ensureDirectories(),this.recentAlerts=new Map,this.alertDedupeWindowMs=e.alertDedupeWindowMs||6e4}async sendAlert(e){const t=this._normalizeAlert(e);if(this._isDuplicate(t))return{sent:!1,reason:"duplicate"};this._recordAlert(t);const s={console:!1,file:!1,slack:!1,email:!1};return this.config.consoleOutput&&(s.console=this._outputToConsole(t)),this.config.fileOutput&&(s.file=await this._outputToFile(t)),this.config.slackWebhook&&["critical","error"].includes(t.level)&&(s.slack=await this._outputToSlack(t)),this.emit("alertSent",{alert:t,results:s}),{sent:!0,alert:t,results:s}}async sendDigest(e){if(console.log("\n"+"=".repeat(60)),console.log("[SENTINEL] DAILY DIGEST - "+e.date),console.log("=".repeat(60)),console.log("\nSummary:"),console.log(` Issues Found: ${e.summary.issuesFound}`),console.log(` Critical: ${e.summary.criticalIssues}`),console.log(` Warnings: ${e.summary.warningIssues}`),console.log(` Files Watched: ${e.summary.filesWatched}`),console.log(` Last Scan: ${e.summary.lastScan||"N/A"}`),e.topIssues&&e.topIssues.length>0){console.log("\nTop Issues:");for(const t of e.topIssues.slice(0,5))console.log(` [${t.severity.toUpperCase()}] ${t.message}`),t.file&&console.log(` File: ${path.relative(this.config.rootPath,t.file)}`)}if(e.recommendations&&e.recommendations.length>0){console.log("\nRecommendations:");for(const t of e.recommendations)console.log(` [${t.priority.toUpperCase()}] ${t.message}`)}if(console.log("\n"+"=".repeat(60)+"\n"),this.config.fileOutput){const t=path.join(this.config.reportsDir,`digest-${e.date}.json`);fs.writeFileSync(t,JSON.stringify(e,null,2))}return this.emit("digestSent",e),{sent:!0}}async sendWeeklyReport(e){if(console.log("\n"+"=".repeat(70)),console.log("[SENTINEL] WEEKLY REPORT - Week of "+e.weekOf),console.log("=".repeat(70)),console.log("\nMetrics:"),console.log(` Total Scans: ${e.summary.totalScans}`),console.log(` Issues Detected: ${e.summary.issuesDetected}`),console.log(` Issues Resolved: ${e.summary.issuesResolved}`),console.log(` Avg Scan Time: ${e.summary.averageScanTime}ms`),console.log("\nCode Quality:"),console.log(` Score: ${e.codeQuality.score}/100 (${e.codeQuality.grade})`),console.log(` Complexity: ${e.codeQuality.factors.complexity}`),console.log(` Maintainability: ${e.codeQuality.factors.maintainability}`),console.log(` Test Coverage: ${e.codeQuality.factors.testCoverage}%`),console.log("\nTechnical Debt:"),console.log(` Estimated Hours: ${e.technicalDebt.estimatedHours}`),console.log(` Refactoring: ${e.technicalDebt.categories.refactoring}h`),console.log(` Documentation: ${e.technicalDebt.categories.documentation}h`),console.log(` Testing: ${e.technicalDebt.categories.testing}h`),e.recommendations&&e.recommendations.length>0){console.log("\nRecommendations:");for(const t of e.recommendations)console.log(` [${t.priority.toUpperCase()}] ${t.message}`)}if(console.log("\n"+"=".repeat(70)+"\n"),this.config.fileOutput){const t=path.join(this.config.reportsDir,`weekly-report-${e.weekOf}.json`);fs.writeFileSync(t,JSON.stringify(e,null,2))}return this.emit("weeklyReportSent",e),{sent:!0}}getAlertHistory(e={}){const{days:t=7,level:s=null,category:o=null}=e,r=new Date;r.setDate(r.getDate()-t);const l=[];try{if(fs.existsSync(this.config.alertsDir)){const e=fs.readdirSync(this.config.alertsDir).filter(e=>e.startsWith("alerts-")&&e.endsWith(".json")).sort().reverse();for(const t of e){const e=t.match(/alerts-(\d{4}-\d{2}-\d{2})/);if(e&&new Date(e[1])<r)break;try{const e=JSON.parse(fs.readFileSync(path.join(this.config.alertsDir,t),"utf8"));l.push(...e.alerts||[])}catch(e){}}}}catch(e){console.error("[ALERT-MANAGER] Error reading alert history:",e.message)}let n=l;return s&&(n=n.filter(e=>e.level===s)),o&&(n=n.filter(e=>e.category===o)),n}async clearOldAlerts(e=30){const t=new Date;t.setDate(t.getDate()-e);let s=0;try{if(fs.existsSync(this.config.alertsDir)){const e=fs.readdirSync(this.config.alertsDir);for(const o of e){const e=o.match(/(\d{4}-\d{2}-\d{2})/);e&&new Date(e[1])<t&&(fs.unlinkSync(path.join(this.config.alertsDir,o)),s++)}}}catch(e){console.error("[ALERT-MANAGER] Error clearing old alerts:",e.message)}return{deleted:s}}_ensureDirectories(){try{fs.existsSync(this.config.alertsDir)||fs.mkdirSync(this.config.alertsDir,{recursive:!0}),fs.existsSync(this.config.reportsDir)||fs.mkdirSync(this.config.reportsDir,{recursive:!0})}catch(e){console.warn("[ALERT-MANAGER] Could not create directories:",e.message)}}_normalizeAlert(e){return{id:e.id||this._generateAlertId(),timestamp:e.timestamp||(new Date).toISOString(),level:e.level||"info",category:e.category||"general",title:e.title||"Alert",message:e.message||"",file:e.file||null,line:e.line||null,details:e.details||{}}}_generateAlertId(){return`alert-${Date.now()}-${Math.random().toString(36).substr(2,9)}`}_isDuplicate(e){const t=`${e.level}:${e.message}:${e.file}`,s=this.recentAlerts.get(t);return!!(s&&Date.now()-s<this.alertDedupeWindowMs)}_recordAlert(e){const t=`${e.level}:${e.message}:${e.file}`;this.recentAlerts.set(t,Date.now());const s=Date.now()-this.alertDedupeWindowMs;for(const[e,t]of this.recentAlerts)t<s&&this.recentAlerts.delete(e)}_outputToConsole(e){const t={critical:"",error:"",warning:"",info:""}[e.level]||"";if(console.log(`${t}[SENTINEL:${e.level.toUpperCase()}] ${e.title}`),console.log(` ${e.message}`),e.file){const t=path.relative(this.config.rootPath,e.file);console.log(` File: ${t}${e.line?":"+e.line:""}`)}return!0}async _outputToFile(e){try{const t=(new Date).toISOString().split("T")[0],s=path.join(this.config.alertsDir,`alerts-${t}.json`);let o={date:t,alerts:[]};if(fs.existsSync(s))try{o=JSON.parse(fs.readFileSync(s,"utf8"))}catch(e){}return o.alerts.push(e),o.alerts.length>this.config.maxAlertsPerFile&&(o.alerts=o.alerts.slice(-this.config.maxAlertsPerFile)),fs.writeFileSync(s,JSON.stringify(o,null,2)),!0}catch(e){return console.error("[ALERT-MANAGER] File output failed:",e.message),!1}}async _outputToSlack(e){if(!this.config.slackWebhook)return!1;try{return console.log(`[ALERT-MANAGER] Would send to Slack: ${e.title}`),!0}catch(e){return console.error("[ALERT-MANAGER] Slack output failed:",e.message),!1}}async _createGitHubIssue(e){return console.log(`[ALERT-MANAGER] Would create GitHub issue: ${e.title}`),!1}}module.exports=AlertManager;
1
+ const fs=require("fs"),path=require("path"),EventEmitter=require("events");class AlertManager extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),alertsDir:e.alertsDir||path.join(e.rootPath||process.cwd(),"sentinel","alerts"),reportsDir:e.reportsDir||path.join(e.rootPath||process.cwd(),"sentinel","reports"),consoleOutput:!1!==e.consoleOutput,fileOutput:!1!==e.fileOutput,maxAlertsPerFile:e.maxAlertsPerFile||1e3,slackWebhook:e.slackWebhook||null,emailConfig:e.emailConfig||null,...e},this._ensureDirectories(),this.recentAlerts=new Map,this.alertDedupeWindowMs=e.alertDedupeWindowMs||6e4}async sendAlert(e){const t=this._normalizeAlert(e);if(this._isDuplicate(t))return{sent:!1,reason:"duplicate"};this._recordAlert(t);const s={console:!1,file:!1,slack:!1,email:!1};return this.config.consoleOutput&&(s.console=this._outputToConsole(t)),this.config.fileOutput&&(s.file=await this._outputToFile(t)),this.config.slackWebhook&&["critical","error"].includes(t.level)&&(s.slack=await this._outputToSlack(t)),this.emit("alertSent",{alert:t,results:s}),{sent:!0,alert:t,results:s}}async sendDigest(e){if(console.log("\n"+"=".repeat(60)),console.log("[SENTINEL] DAILY DIGEST - "+e.date),console.log("=".repeat(60)),console.log("\nSummary:"),console.log(` Issues Found: ${e.summary.issuesFound}`),console.log(` Critical: ${e.summary.criticalIssues}`),console.log(` Warnings: ${e.summary.warningIssues}`),console.log(` Files Watched: ${e.summary.filesWatched}`),console.log(` Last Scan: ${e.summary.lastScan||"N/A"}`),e.topIssues&&e.topIssues.length>0){console.log("\nTop Issues:");for(const t of e.topIssues.slice(0,5))console.log(` [${t.severity.toUpperCase()}] ${t.message}`),t.file&&console.log(` File: ${path.relative(this.config.rootPath,t.file)}`)}if(e.recommendations&&e.recommendations.length>0){console.log("\nRecommendations:");for(const t of e.recommendations)console.log(` [${t.priority.toUpperCase()}] ${t.message}`)}if(console.log("\n"+"=".repeat(60)+"\n"),this.config.fileOutput){const t=path.join(this.config.reportsDir,`digest-${e.date}.json`);fs.writeFileSync(t,JSON.stringify(e,null,2))}return this.emit("digestSent",e),{sent:!0}}async sendWeeklyReport(e){if(console.log("\n"+"=".repeat(70)),console.log("[SENTINEL] WEEKLY REPORT - Week of "+e.weekOf),console.log("=".repeat(70)),console.log("\nMetrics:"),console.log(` Total Scans: ${e.summary.totalScans}`),console.log(` Issues Detected: ${e.summary.issuesDetected}`),console.log(` Issues Resolved: ${e.summary.issuesResolved}`),console.log(` Avg Scan Time: ${e.summary.averageScanTime}ms`),console.log("\nCode Quality:"),console.log(` Score: ${e.codeQuality.score}/100 (${e.codeQuality.grade})`),console.log(` Complexity: ${e.codeQuality.factors.complexity}`),console.log(` Maintainability: ${e.codeQuality.factors.maintainability}`),console.log(` Test Coverage: ${e.codeQuality.factors.testCoverage}%`),console.log("\nTechnical Debt:"),console.log(` Estimated Hours: ${e.technicalDebt.estimatedHours}`),console.log(` Refactoring: ${e.technicalDebt.categories.refactoring}h`),console.log(` Documentation: ${e.technicalDebt.categories.documentation}h`),console.log(` Testing: ${e.technicalDebt.categories.testing}h`),e.recommendations&&e.recommendations.length>0){console.log("\nRecommendations:");for(const t of e.recommendations)console.log(` [${t.priority.toUpperCase()}] ${t.message}`)}if(console.log("\n"+"=".repeat(70)+"\n"),this.config.fileOutput){const t=path.join(this.config.reportsDir,`weekly-report-${e.weekOf}.json`);fs.writeFileSync(t,JSON.stringify(e,null,2))}return this.emit("weeklyReportSent",e),{sent:!0}}getAlertHistory(e={}){const{days:t=7,level:s=null,category:o=null}=e,r=new Date;r.setDate(r.getDate()-t);const l=[];try{if(fs.existsSync(this.config.alertsDir)){const e=fs.readdirSync(this.config.alertsDir).filter(e=>e.startsWith("alerts-")&&e.endsWith(".json")).sort().reverse();for(const t of e){const e=t.match(/alerts-(\d{4}-\d{2}-\d{2})/);if(e&&new Date(e[1])<r)break;try{const e=JSON.parse(fs.readFileSync(path.join(this.config.alertsDir,t),"utf8"));l.push(...e.alerts||[])}catch(e){}}}}catch(e){console.error("[ALERT-MANAGER] Error reading alert history")}let n=l;return s&&(n=n.filter(e=>e.level===s)),o&&(n=n.filter(e=>e.category===o)),n}async clearOldAlerts(e=30){const t=new Date;t.setDate(t.getDate()-e);let s=0;try{if(fs.existsSync(this.config.alertsDir)){const e=fs.readdirSync(this.config.alertsDir);for(const o of e){const e=o.match(/(\d{4}-\d{2}-\d{2})/);e&&new Date(e[1])<t&&(fs.unlinkSync(path.join(this.config.alertsDir,o)),s++)}}}catch(e){console.error("[ALERT-MANAGER] Error clearing old alerts")}return{deleted:s}}_ensureDirectories(){try{fs.existsSync(this.config.alertsDir)||fs.mkdirSync(this.config.alertsDir,{recursive:!0}),fs.existsSync(this.config.reportsDir)||fs.mkdirSync(this.config.reportsDir,{recursive:!0})}catch(e){console.warn("[ALERT-MANAGER] Could not create directories")}}_normalizeAlert(e){return{id:e.id||this._generateAlertId(),timestamp:e.timestamp||(new Date).toISOString(),level:e.level||"info",category:e.category||"general",title:e.title||"Alert",message:e.message||"",file:e.file||null,line:e.line||null,details:e.details||{}}}_generateAlertId(){return`alert-${Date.now()}-${Math.random().toString(36).substr(2,9)}`}_isDuplicate(e){const t=`${e.level}:${e.message}:${e.file}`,s=this.recentAlerts.get(t);return!!(s&&Date.now()-s<this.alertDedupeWindowMs)}_recordAlert(e){const t=`${e.level}:${e.message}:${e.file}`;this.recentAlerts.set(t,Date.now());const s=Date.now()-this.alertDedupeWindowMs;for(const[e,t]of this.recentAlerts)t<s&&this.recentAlerts.delete(e)}_outputToConsole(e){const t={critical:"",error:"",warning:"",info:""}[e.level]||"";if(console.log(`${t}[SENTINEL:${e.level.toUpperCase()}] ${e.title}`),console.log(` ${e.message}`),e.file){const t=path.relative(this.config.rootPath,e.file);console.log(` File: ${t}${e.line?":"+e.line:""}`)}return!0}async _outputToFile(e){try{const t=(new Date).toISOString().split("T")[0],s=path.join(this.config.alertsDir,`alerts-${t}.json`);let o={date:t,alerts:[]};if(fs.existsSync(s))try{o=JSON.parse(fs.readFileSync(s,"utf8"))}catch(e){}return o.alerts.push(e),o.alerts.length>this.config.maxAlertsPerFile&&(o.alerts=o.alerts.slice(-this.config.maxAlertsPerFile)),fs.writeFileSync(s,JSON.stringify(o,null,2)),!0}catch(e){return console.error("[ALERT-MANAGER] File output failed"),!1}}async _outputToSlack(e){if(!this.config.slackWebhook)return!1;try{return console.log(`[ALERT-MANAGER] Would send to Slack: ${e.title}`),!0}catch(e){return console.error("[ALERT-MANAGER] Slack output failed"),!1}}async _createGitHubIssue(e){return console.log(`[ALERT-MANAGER] Would create GitHub issue: ${e.title}`),!1}}module.exports=AlertManager;
@@ -0,0 +1 @@
1
+ "use strict";const parser=require("@babel/parser"),path=require("path"),pythonAstAnalyzer=require("./python-ast-analyzer"),goAstAnalyzer=require("./go-ast-analyzer"),BABEL_PLUGINS=["estree","typescript","jsx","classProperties","decorators-legacy"];function walk(e,t){if(e&&"object"==typeof e&&"string"==typeof e.type){t(e);for(const n in e){if("loc"===n||"range"===n||"start"===n||"end"===n)continue;const s=e[n];if(Array.isArray(s))for(const e of s)e&&"object"==typeof e&&"string"==typeof e.type&&walk(e,t);else s&&"object"==typeof s&&"string"==typeof s.type&&walk(s,t)}}}function lineOf(e){return e&&e.loc?e.loc.start.line:0}function snippet(e,t){return!t||t<1||t>e.length?"":e[t-1].trim().substring(0,100)}function mkIssue(e,t,n,s){return{file:e,line:t,category:"security",type:s.type,severity:s.severity,id:s.id,name:s.name,message:s.message,code:snippet(n,t),source:"ast"}}const CMD_FUNCS=new Set(["exec","execSync","spawn","spawnSync","fork"]),SQL_METHODS=new Set(["query","execute","raw","where"]);function isNonLiteralDynamic(e){let t=!1;return walk(e,e=>{"Identifier"!==e.type&&"CallExpression"!==e.type&&"MemberExpression"!==e.type||(t=!0)}),t}function isDynamicStringBuild(e){return!!e&&("TemplateLiteral"===e.type?(e.expressions||[]).length>0:"BinaryExpression"===e.type&&"+"===e.operator&&isNonLiteralDynamic(e))}function collectEvalAliases(e){const t=new Set;let n=!0,s=0;for(;n&&s<5;)n=!1,s++,walk(e,e=>{if("VariableDeclarator"===e.type&&e.init&&"Identifier"===e.init.type&&"Identifier"===e.id.type){const s=e.init.name;"eval"!==s&&!t.has(s)||t.has(e.id.name)||(t.add(e.id.name),n=!0)}if("AssignmentExpression"===e.type&&"="===e.operator&&"Identifier"===e.left.type&&"Identifier"===e.right.type){const s=e.right.name;"eval"!==s&&!t.has(s)||t.has(e.left.name)||(t.add(e.left.name),n=!0)}});return t}function findProtoGuard(e){let t=!1;return walk(e,e=>{"Literal"===e.type&&"string"==typeof e.value&&["__proto__","constructor","prototype"].includes(e.value)&&(t=!0)}),t}function analyzeJSAst(e,t){const n=[],s=t.split("\n");let r;try{r=parser.parse(t,{sourceType:"unambiguous",plugins:BABEL_PLUGINS,allowReturnOutsideFunction:!0,allowImportExportEverywhere:!0,allowAwaitOutsideFunction:!0,allowSuperOutsideMethod:!0,allowUndeclaredExports:!0,errorRecovery:!1}).program}catch(e){return n}const i=collectEvalAliases(r);return walk(r,t=>{if("CallExpression"===t.type&&"Identifier"===t.callee.type){const r=t.callee.name;if("eval"===r||i.has(r)){const i="eval"!==r;n.push(mkIssue(e,lineOf(t),s,{type:"eval_abuse",severity:"error",id:"AST001",name:i?"Aliased eval() Usage (AST)":"eval() Usage (AST)",message:i?`eval() called indirectly through alias "${r}" - AST-detected`:"eval() is dangerous and should be avoided (AST-confirmed)"}))}}if("CallExpression"===t.type&&"MemberExpression"===t.callee.type&&"Identifier"===t.callee.property.type&&SQL_METHODS.has(t.callee.property.name)){const r=t.arguments[0];r&&isDynamicStringBuild(r)&&n.push(mkIssue(e,lineOf(t),s,{type:"sql_injection",severity:"error",id:"AST002",name:"SQL Injection Risk (AST)",message:`Dynamic string passed to .${t.callee.property.name}() - use parameterized queries`}))}if("AssignmentExpression"!==t.type||"MemberExpression"!==t.left.type||"Identifier"!==t.left.property.type||"innerHTML"!==t.left.property.name&&"outerHTML"!==t.left.property.name||"Literal"!==t.right.type&&n.push(mkIssue(e,lineOf(t),s,{type:"xss",severity:"warning",id:"AST003",name:`${t.left.property.name} Assignment (AST)`,message:`Non-literal value assigned to ${t.left.property.name} - possible XSS`})),"CallExpression"===t.type&&"MemberExpression"===t.callee.type&&"Identifier"===t.callee.object.type&&"document"===t.callee.object.name&&"Identifier"===t.callee.property.type&&"write"===t.callee.property.name){const r=t.arguments[0];r&&"Literal"!==r.type&&n.push(mkIssue(e,lineOf(t),s,{type:"xss",severity:"warning",id:"AST003",name:"document.write() Assignment (AST)",message:"Non-literal value passed to document.write() - possible XSS"}))}if("CallExpression"===t.type){let r=null;if("Identifier"===t.callee.type?r=t.callee.name:"MemberExpression"===t.callee.type&&"Identifier"===t.callee.property.type&&(r=t.callee.property.name),r&&CMD_FUNCS.has(r)){const i=t.arguments[0];i&&isDynamicStringBuild(i)&&n.push(mkIssue(e,lineOf(t),s,{type:"command_injection",severity:"error",id:"AST004",name:`${r}() Command Injection (AST)`,message:`Dynamic string passed to ${r}() - command injection risk`}))}}if("ForInStatement"===t.type||"ForOfStatement"===t.type){let r=null;if(("ForInStatement"===t.type||"CallExpression"===t.right.type&&"MemberExpression"===t.right.callee.type&&"Identifier"===t.right.callee.object.type&&"Object"===t.right.callee.object.name&&"Identifier"===t.right.callee.property.type&&"keys"===t.right.callee.property.name)&&("VariableDeclaration"===t.left.type?r=t.left.declarations[0].id.name:"Identifier"===t.left.type&&(r=t.left.name)),r&&!findProtoGuard(t.body)){let i=!1;walk(t.body,e=>{"AssignmentExpression"===e.type&&"MemberExpression"===e.left.type&&e.left.computed&&"Identifier"===e.left.property.type&&e.left.property.name===r&&(i=!0)}),i&&n.push(mkIssue(e,lineOf(t),s,{type:"prototype_pollution",severity:"error",id:"AST005",name:"Unguarded Object Merge (AST)",message:`Loop assigns to obj[${r}] without checking for __proto__/constructor/prototype - prototype pollution risk`}))}}}),n}function joinLogicalStatements(e){const t=e.split("\n"),n=[];let s="",r=0,i=0;for(let e=0;e<t.length;e++){const a=t[e];s||(r=e+1),s+=(s?"\n":"")+a;for(const e of a)"("===e||"["===e||"{"===e?i++:")"!==e&&"]"!==e&&"}"!==e||(i=Math.max(0,i-1));const o=/\\\s*$/.test(a);0!==i||o||(n.push({text:s,startLine:r}),s="")}return s&&n.push({text:s,startLine:r}),n}function extractBalanced(e,t){let n=0;for(let s=t;s<e.length;s++)if("("===e[s])n++;else if(")"===e[s]&&(n--,0===n))return e.slice(t+1,s);return e.slice(t+1)}function splitTopLevelArgs(e){const t=[];let n=0,s="",r=null;for(let i=0;i<e.length;i++){const a=e[i];r?(s+=a,a===r&&"\\"!==e[i-1]&&(r=null)):'"'!==a&&"'"!==a?("([{".includes(a)&&n++,")]}".includes(a)&&n--,","!==a||0!==n?s+=a:(t.push(s.trim()),s="")):(r=a,s+=a)}return s.trim()&&t.push(s.trim()),t}function isDynamicPyString(e){if(!e)return!1;const t=e.trim();return!(!/^[rR]?[fF]['"]/.test(t)||!t.includes("{"))||!!/['"]\s*%\s*\(?[\w.]/.test(t)||!!/['"]\s*\+\s*\w|\w\s*\+\s*['"]/.test(t)}function analyzePythonHeuristic(e,t){const n=[],s=t.split("\n"),r=joinLogicalStatements(t),i=new Set;for(const e of r){const t=e.text.match(/^\s*([A-Za-z_]\w*)\s*=\s*(eval|exec)\s*$/m);t&&i.add(t[1])}for(const t of r){const r=t.text,a=t.startLine;for(const t of i)new RegExp("\\b"+t+"\\s*\\(").test(r)&&n.push(mkIssue(e,a,s,{type:"eval_abuse",severity:"error",id:"AST011",name:"Aliased eval/exec Usage (AST)",message:`eval/exec called indirectly through alias "${t}" - AST-detected`}));const o=r.search(/\bos\.system\s*\(/);if(-1!==o){const t=r.indexOf("(",o);isDynamicPyString(extractBalanced(r,t))&&n.push(mkIssue(e,a,s,{type:"command_injection",severity:"error",id:"AST012",name:"os.system() Command Injection (AST)",message:"Dynamic string passed to os.system() - command injection risk"}))}const l=r.match(/\bsubprocess\.(call|run|Popen|check_output)\s*\(/);if(l){const t=r.indexOf("(",l.index),i=extractBalanced(r,t);if(/shell\s*=\s*True/.test(i)){const t=splitTopLevelArgs(i)[0]||"";isDynamicPyString(t)&&!t.trim().startsWith("[")&&n.push(mkIssue(e,a,s,{type:"command_injection",severity:"error",id:"AST013",name:"subprocess shell=True Command Injection (AST)",message:`Dynamic string passed to subprocess.${l[1]}() with shell=True`}))}}const c=r.match(/\b(?:cursor|db|conn|connection)\.execute\s*\(/);if(c){const t=r.indexOf("(",c.index);isDynamicPyString(splitTopLevelArgs(extractBalanced(r,t))[0]||"")&&n.push(mkIssue(e,a,s,{type:"sql_injection",severity:"error",id:"AST014",name:"SQL Injection Risk (AST)",message:"Dynamic string passed to .execute() - use parameterized queries"}))}}return n}const JS_EXTS=new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs"]),PY_EXTS=new Set([".py",".pyw"]),GO_EXTS=new Set([".go"]),MAX_AST_FILE_SIZE=2e4;function analyzePython(e,t){const n=analyzePythonHeuristic(e,t);let s=[];try{s=pythonAstAnalyzer.analyze(e,t)}catch(e){s=[]}return n.concat(s)}function analyze(e,t){if(t.length>2e4)return[];const n=path.extname(e).toLowerCase();try{if(JS_EXTS.has(n))return analyzeJSAst(e,t);if(PY_EXTS.has(n))return analyzePython(e,t);if(GO_EXTS.has(n))return analyzeGo(e,t)}catch(e){return[]}return[]}function analyzeGo(e,t){try{return goAstAnalyzer.analyze(e,t)}catch(e){return[]}}module.exports={analyze:analyze,analyzeJSAst:analyzeJSAst,analyzePythonHeuristic:analyzePythonHeuristic,analyzePython:analyzePython,analyzeGo:analyzeGo,_internal:{walk:walk,isDynamicStringBuild:isDynamicStringBuild,collectEvalAliases:collectEvalAliases,joinLogicalStatements:joinLogicalStatements,extractBalanced:extractBalanced,splitTopLevelArgs:splitTopLevelArgs,isDynamicPyString:isDynamicPyString}};
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),crypto=require("crypto"),BASELINE_FILE=".thuban-baseline.json";class BaselineManager{constructor(e){this.rootPath=e,this.baselinePath=path.join(e,BASELINE_FILE)}create(e){const s={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 fs.writeFileSync(this.baselinePath,JSON.stringify(s,null,2),"utf-8"),{path:this.baselinePath,count:e.length}}load(){try{return JSON.parse(fs.readFileSync(this.baselinePath,"utf-8"))}catch(e){return null}}filterNew(e){const s=this.load();if(!s||!s.issues)return{newIssues:e,baselinedIssues:[],baselineExists:!1};const t=new Set(s.issues.map(e=>e.fingerprint)),n=[],i=[];for(const s of e){const e=this._fingerprint(s);t.has(e)?i.push(s):n.push(s)}return{newIssues:n,baselinedIssues:i,baselineExists:!0,baselineDate:s.created,baselineCount:s.issueCount}}exists(){return fs.existsSync(this.baselinePath)}_fingerprint(e){const s=[e.file||"",e.category||"",e.id||"",(e.message||"").substring(0,100)].join("|");return crypto.createHash("sha256").update(s).digest("hex").substring(0,16)}}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;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;
@@ -0,0 +1 @@
1
+ "use strict";const parser=require("@babel/parser"),path=require("path"),{walk:walk}=require("./ast-analyzer")._internal,BABEL_PLUGINS=["estree","typescript","jsx","classProperties","decorators-legacy"],MAX_CFG_FILE_SIZE=2e4;function lineOf(e){return e&&e.loc?e.loc.start.line:0}function snippet(e,t){return!t||t<1||t>e.length?"":e[t-1].trim().substring(0,100)}function mkIssue(e,t,n,a){return{file:e,line:t,category:"quality",type:a.type,severity:a.severity,id:a.id,name:a.name,message:a.message,code:snippet(n,t),source:"cfg"}}function isFunctionNode(e){return"FunctionDeclaration"===e.type||"FunctionExpression"===e.type||"ArrowFunctionExpression"===e.type}const LOOP_TYPES=new Set(["WhileStatement","ForStatement","DoWhileStatement","ForInStatement","ForOfStatement"]);function staticTruthiness(e){if(!e)return null;if("Literal"===e.type)return null!==e.value&&("boolean"==typeof e.value?e.value:"number"==typeof e.value?0!==e.value&&!Number.isNaN(e.value):"string"==typeof e.value?e.value.length>0:null);if("UnaryExpression"===e.type&&"!"===e.operator){const t=staticTruthiness(e.argument);return null===t?null:!t}return null}function loopHasExit(e){let t=!1;return function e(n,a){if(t||!n||"object"!=typeof n||"string"!=typeof n.type)return;if(isFunctionNode(n))return;if("ReturnStatement"===n.type||"ThrowStatement"===n.type)return void(t=!0);if("BreakStatement"===n.type||"ContinueStatement"===n.type)return void((n.label||0===a)&&(t=!0));const r=LOOP_TYPES.has(n.type)||"SwitchStatement"===n.type?a+1:a;for(const t in n){if("loc"===t||"range"===t||"start"===t||"end"===t)continue;const a=n[t];if(Array.isArray(a))for(const t of a)t&&"object"==typeof t&&"string"==typeof t.type&&e(t,r);else a&&"object"==typeof a&&"string"==typeof a.type&&e(a,r)}}(e,0),t}function isStaticallyInfinite(e){return"ForStatement"===e.type?!e.test:("WhileStatement"===e.type||"DoWhileStatement"===e.type)&&!0===staticTruthiness(e.test)}function blockTerminates(e){let t=!1;for(const n of e){if(t)break;terminates(n)&&(t=!0)}return t}function terminates(e){switch(e.type){case"ReturnStatement":case"ThrowStatement":case"BreakStatement":case"ContinueStatement":return!0;case"BlockStatement":return blockTerminates(e.body);case"IfStatement":return!!e.alternate&&terminates(e.consequent)&&terminates(e.alternate);case"TryStatement":return!(!e.finalizer||!blockTerminates(e.finalizer.body))||!!e.handler&&blockTerminates(e.block.body)&&blockTerminates(e.handler.body.body);case"WhileStatement":case"ForStatement":case"DoWhileStatement":return isStaticallyInfinite(e)&&!loopHasExit(e.body);case"LabeledStatement":return terminates(e.body);default:return!1}}function isNoiseStatement(e){return"FunctionDeclaration"===e.type||"EmptyStatement"===e.type}function scanBlockBody(e,t,n,a){let r=!1;for(const i of e){if(r){isNoiseStatement(i)||a.push(mkIssue(t,lineOf(i),n,{type:"unreachable_code",severity:"warning",id:"CFG001",name:"Unreachable Code (CFG)",message:"This code can never execute - it follows a return/throw/break/continue (or an if/else where both branches always exit)"}));break}scanStatement(i,t,n,a),terminates(i)&&(r=!0)}}function scanStatement(e,t,n,a){switch(e.type){case"BlockStatement":scanBlockBody(e.body,t,n,a);break;case"IfStatement":scanStatement(e.consequent,t,n,a),e.alternate&&scanStatement(e.alternate,t,n,a);break;case"WhileStatement":case"DoWhileStatement":case"ForStatement":case"ForInStatement":case"ForOfStatement":case"LabeledStatement":scanStatement(e.body,t,n,a);break;case"SwitchStatement":for(const r of e.cases)scanBlockBody(r.consequent,t,n,a);break;case"TryStatement":scanBlockBody(e.block.body,t,n,a),e.handler&&scanBlockBody(e.handler.body.body,t,n,a),e.finalizer&&scanBlockBody(e.finalizer.body,t,n,a)}}function analyzeReachability(e,t,n,a){scanBlockBody(e.body,t,n,a),walk(e,e=>{isFunctionNode(e)&&e.body&&"BlockStatement"===e.body.type&&scanBlockBody(e.body.body,t,n,a)})}function analyzeLoopsAndBranches(e,t,n,a){walk(e,e=>{if(!LOOP_TYPES.has(e.type)||"WhileStatement"!==e.type&&"ForStatement"!==e.type&&"DoWhileStatement"!==e.type||isStaticallyInfinite(e)&&!loopHasExit(e.body)&&a.push(mkIssue(t,lineOf(e),n,{type:"infinite_loop",severity:"warning",id:"CFG002",name:"Infinite Loop With No Exit (CFG)",message:"Loop condition is always true and no break/return/throw was found anywhere inside it - this will hang"})),"IfStatement"===e.type){const r=staticTruthiness(e.test);null!==r&&a.push(mkIssue(t,lineOf(e),n,{type:"dead_branch",severity:"warning",id:"CFG003",name:"Dead Branch (CFG)",message:r?"Condition is always truthy - the else branch (if any) can never execute":e.alternate?"Condition is always falsy - the if branch can never execute, only else runs":"Condition is always falsy - this if branch can never execute"}))}if("ConditionalExpression"===e.type){const r=staticTruthiness(e.test);null!==r&&a.push(mkIssue(t,lineOf(e),n,{type:"dead_branch",severity:"warning",id:"CFG003",name:"Dead Branch (CFG)",message:r?"Ternary condition is always truthy - the alternate expression can never be reached":"Ternary condition is always falsy - the consequent expression can never be reached"}))}"WhileStatement"!==e.type&&"ForStatement"!==e.type||!e.test||!1===staticTruthiness(e.test)&&a.push(mkIssue(t,lineOf(e),n,{type:"dead_loop",severity:"warning",id:"CFG004",name:"Dead Loop (CFG)",message:"Loop condition is always false - the loop body will never execute, not even once"}))})}const FS_SYNC_THROWING=new Set(["readFileSync","writeFileSync","appendFileSync","readdirSync","statSync","lstatSync","unlinkSync","mkdirSync","rmdirSync","rmSync","renameSync","copyFileSync","accessSync","chmodSync","chownSync","symlinkSync","readlinkSync","realpathSync","openSync","closeSync","readSync","writeSync","truncateSync","utimesSync","mkdtempSync"]);function isKnownThrowingCall(e){if("CallExpression"!==e.type||"MemberExpression"!==e.callee.type)return null;const{object:t,property:n}=e.callee;return"Identifier"!==n.type?null:"Identifier"===t.type&&"JSON"===t.name&&"parse"===n.name?"JSON.parse":"Identifier"===t.type&&"fs"===t.name&&FS_SYNC_THROWING.has(n.name)?`fs.${n.name}`:null}function analyzeExceptionGaps(e,t,n,a){!function e(r,i){if(!r||"object"!=typeof r||"string"!=typeof r.type)return;if("TryStatement"===r.type)return e(r.block,!!r.handler),r.handler&&e(r.handler.body,i),void(r.finalizer&&e(r.finalizer,i));if(isFunctionNode(r))return void e(r.body,!1);const o=isKnownThrowingCall(r);o&&!i&&a.push(mkIssue(t,lineOf(r),n,{type:"exception_handling_gap",severity:"warning",id:"CFG005",name:"Missing Error Handling (CFG)",message:`${o}() can throw and is not wrapped in a try/catch`}));for(const t in r){if("loc"===t||"range"===t||"start"===t||"end"===t)continue;const n=r[t];if(Array.isArray(n))for(const t of n)t&&"object"==typeof t&&"string"==typeof t.type&&e(t,i);else n&&"object"==typeof n&&"string"==typeof n.type&&e(n,i)}}(e,!1)}const JS_EXTS=new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs"]);function analyzeJSCfg(e,t){const n=[],a=t.split("\n");let r;try{r=parser.parse(t,{sourceType:"unambiguous",plugins:BABEL_PLUGINS,allowReturnOutsideFunction:!0,allowImportExportEverywhere:!0,allowAwaitOutsideFunction:!0,allowSuperOutsideMethod:!0,allowUndeclaredExports:!0,errorRecovery:!1}).program}catch(e){return n}return analyzeReachability(r,e,a,n),analyzeLoopsAndBranches(r,e,a,n),analyzeExceptionGaps(r,e,a,n),n}function analyze(e,t){if(t.length>2e4)return[];const n=path.extname(e).toLowerCase();try{if(JS_EXTS.has(n))return analyzeJSCfg(e,t)}catch(e){return[]}return[]}module.exports={analyze:analyze,analyzeJSCfg:analyzeJSCfg,_internal:{staticTruthiness:staticTruthiness,loopHasExit:loopHasExit,terminates:terminates,blockTerminates:blockTerminates,isKnownThrowingCall:isKnownThrowingCall}};
@@ -1 +1 @@
1
- const fs=require("fs"),path=require("path"),EventEmitter=require("events");class CodeScanner extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),ignorePatterns:e.ignorePatterns||["node_modules/**",".git/**","dist/**",".orion/snapshots/**"],maxFileSize:e.maxFileSize||1048576,complexityThreshold:e.complexityThreshold||15,nestingThreshold:e.nestingThreshold||4,...e},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=[{id:"SEC001",name:"Hardcoded API Key",pattern:/['"](?:sk-|pk-|xox[pboa]-|AKIA|ghp_|gho_|github_pat_)[a-zA-Z0-9_-]{10,}['"]|['"][a-zA-Z0-9]{32,}['"]/,context:/(api[_-]?key|apikey|secret[_\s]*key|token|credential|bearer)/i,severity:"critical",message:"Possible hardcoded API key or secret detected",skipIfSafe:!0},{id:"SEC002",name:"Hardcoded Password",pattern:/password\s*[:=]\s*['"][^'"]+['"]/i,severity:"critical",message:"Hardcoded password detected",skipInTests:!0,skipIfSafe:!0},{id:"SEC003",name:"SQL Injection Risk",pattern:/\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|WHERE)\b.*(\$\{|\+\s*['"]|\+\s*\w+|f['"].*\{|%s|%d)/i,severity:"error",message:"Potential SQL injection - use parameterized queries"},{id:"SEC004",name:"eval() Usage",pattern:/\beval\s*\(/,severity:"error",message:"eval() is dangerous and should be avoided"},{id:"SEC005",name:"innerHTML Assignment",pattern:/\.innerHTML\s*=/,severity:"warning",message:"innerHTML can cause XSS - consider using textContent or sanitization"},{id:"SEC006",name:"Disabled Security",pattern:/rejectUnauthorized\s*:\s*false|verify\s*=\s*False|InsecureSkipVerify\s*:\s*true/,severity:"error",message:"SSL certificate validation disabled"},{id:"SEC007",name:"Exposed .env Reference",pattern:/\.env['"]/,context:/fs\.(read|write)|path\.join|open\s*\(|os\.Open/i,severity:"warning",message:"Direct .env file manipulation - ensure not exposed"},{id:"SEC008",name:"Unsafe Block (Rust)",pattern:/\bunsafe\s*\{/,severity:"warning",message:"Rust unsafe block - verify memory safety guarantees"},{id:"SEC009",name:"Shell Execution (PHP/Ruby)",pattern:/\bshell_exec\s*\(|\bpassthru\s*\(|`[^`]+`/,severity:"error",message:"Shell execution function - command injection risk if input unsanitized"}],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,s,t)=>this._checkDependencyExists(e,t)},{id:"AI002",name:"Unused Import",pattern:/const\s+(\w+)\s*=\s*require\(['"][^'"]+['"]\)/,severity:"info",message:"Import may be unused - verify usage",check:(e,s)=>this._checkImportUsage(e,s)},{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(),s={filesScanned:0,issues:[],summary:{},duration:0};try{const t=await this._discoverFiles(this.config.rootPath);console.log(`[CODE-SCANNER] Found ${t.length} files to scan`);for(const e of t){const t=await this.scanFile(e);s.filesScanned++,s.issues.push(...t)}return s.duration=Date.now()-e,s.summary=this._summarizeIssues(s.issues),console.log(`[CODE-SCANNER] Scan complete: ${s.filesScanned} files, ${s.issues.length} issues`),s}catch(e){return console.error("[CODE-SCANNER] Scan failed:",e.message),s.error=e.message,s}}async scanFiles(e){const s={filesScanned:0,issues:[],summary:{}};for(const t of e){const e=await this.scanFile(t);s.filesScanned++,s.issues.push(...e)}return s.summary=this._summarizeIssues(s.issues),s}async scanFile(e){const s=[];try{const t=fs.statSync(e);if(t.isSymbolicLink&&t.isSymbolicLink())return[];if(0===t.size)return[];if(t.size>this.config.maxFileSize)return[{file:e,category:"quality",severity:"info",id:"FILE001",message:`File too large (${Math.round(t.size/1024)}KB) - skipped detailed scan`}];let i;try{i=fs.readFileSync(e)}catch(e){return[]}const n=Math.min(i.length,512);for(let e=0;e<n;e++)if(0===i[e])return[];const r=i.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)&&(s.push(...this._runPatternScan(e,r,a,this.securityPatterns,"security")),s.push(...this._runPatternScan(e,r,a,this.qualityPatterns,"quality")),s.push(...this._runPatternScan(e,r,a,this.performancePatterns,"performance")),s.push(...this._runPatternScan(e,r,a,this.aiPatterns,"ai")),s.push(...this._analyzeComplexity(e,r,a)),s.push(...this._analyzeNesting(e,r,a))),".json"===o&&s.push(...this._scanJson(e,r));for(const e of s)this.emit("issueFound",e);return s}catch(s){return[{file:e,category:"error",severity:"error",id:"SCAN001",message:`Failed to scan file: ${s.message}`}]}}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,s=[]){try{const t=fs.readdirSync(e,{withFileTypes:!0});for(const i of t){const t=path.join(e,i.name),n=path.relative(this.config.rootPath,t);if(!this._shouldIgnore(n))if(i.isDirectory())await this._discoverFiles(t,s);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)&&s.push(t)}}}catch(e){}return s}_shouldIgnore(e){const s=e.replace(/\\/g,"/"),t=["node_modules",".git","dist",".vite"];for(const e of t)if(s.includes("/"+e+"/")||s.startsWith(e+"/")||s===e||s.endsWith("/"+e))return!0;for(const e of this.config.ignorePatterns){const t=e.replace(/\\/g,"/"),i=t.split("/")[0].replace(/\*/g,"");if(i&&(s===i||s.startsWith(i+"/")))return!0;let n=t.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");if(new RegExp("^"+n).test(s))return!0}return!1}_runPatternScan(e,s,t,i,n){const r=[],a=path.basename(e),o=this.testFilePatterns.some(e=>e.test(a))||this.testFilePatterns.some(s=>s.test(e)&&!s.source.includes("\\.js$")),c=path.relative(this.config.rootPath,e).replace(/\\/g,"/");for(const a of i){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(s))continue;let i=0;for(const o of t){i++;const t=o.match(a.pattern);if(t){if(a.skipIfSafe&&this.safePatterns.some(e=>e.test(o)))continue;if(a.check&&!a.check(t,s,e))continue;r.push({file:e,line:i,column:t.index,category:n,severity:a.severity,id:a.id,name:a.name,message:a.message,code:o.trim().substring(0,100)})}}}return r}_analyzeComplexity(e,s,t){const i=[],n=s.match(/function\s+\w+|=>\s*\{|async\s+\w+/g)||[],r=s.match(/\b(if|else|for|while|switch|case|catch|&&|\|\||\?)\b/g)||[],a=n.length>0?r.length/n.length:0;return a>this.config.complexityThreshold&&i.push({file:e,category:"quality",severity:"warning",id:"CMPLX001",name:"High Complexity",message:`Average cyclomatic complexity is ${a.toFixed(1)} (threshold: ${this.config.complexityThreshold})`}),t.length>500&&i.push({file:e,category:"quality",severity:"info",id:"CMPLX002",name:"Long File",message:`File has ${t.length} lines - consider splitting`}),i}_analyzeNesting(e,s,t){const i=[];let n=0,r=0,a=0,o=0;for(const e of t)o++,r+=(e.match(/\{/g)||[]).length-(e.match(/\}/g)||[]).length,r>n&&(n=r,a=o);return n>this.config.nestingThreshold&&i.push({file:e,line:a,category:"quality",severity:"warning",id:"NEST001",name:"Deep Nesting",message:`Max nesting depth is ${n} at line ${a} (threshold: ${this.config.nestingThreshold})`}),i}_scanJson(e,s){const t=[];try{JSON.parse(s)}catch(s){t.push({file:e,category:"quality",severity:"error",id:"JSON001",name:"Invalid JSON",message:`JSON parse error: ${s.message}`})}return/(password|secret|api[_-]?key|token)/i.test(s)&&t.push({file:e,category:"security",severity:"warning",id:"JSON002",name:"Sensitive Data in JSON",message:"Possible sensitive data in JSON file - verify not committed"}),t}_checkDependencyExists(e,s){const t=e[0].match(/require\(['"]([^'"]+)['"]\)/);if(!t)return!0;const i=t[1];if(i.startsWith("."))return!1;if(["fs","path","os","crypto","http","https","events","util","stream","child_process","url","querystring","buffer"].includes(i.split("/")[0]))return!1;try{let e=path.join(this.config.rootPath,"package.json");if(s.includes("server")){const s=path.join(this.config.rootPath,"server","package.json");fs.existsSync(s)&&(e=s)}const t=JSON.parse(fs.readFileSync(e,"utf8")),n={...t.dependencies||{},...t.devDependencies||{}};return!n[i.split("/")[0]]&&!n[i]}catch(e){return!1}}_checkImportUsage(e,s){const t=e[1];if(!t)return!1;const i=new RegExp("\\b"+t+"\\b","g");return(s.match(i)||[]).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,s)=>(e[s.category]=(e[s.category]||0)+1,e),{}),topFiles:this._getTopFiles(e,10)}}_getTopFiles(e,s){const t=e.reduce((e,s)=>(e[s.file]=(e[s.file]||0)+1,e),{});return Object.entries(t).sort((e,s)=>s[1]-e[1]).slice(0,s).map(([e,s])=>({file:path.relative(this.config.rootPath,e),count:s}))}}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"),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 +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=[]){try{const s=fs.readdirSync(e,{withFileTypes:!0});for(const i of s){const s=path.join(e,i.name);if(!this._shouldIgnore(i.name))if(i.isDirectory())await this._discoverFiles(s,t);else if(i.isFile()){const e=path.extname(i.name).toLowerCase();this.config.extensions.includes(e)&&t.push(s)}}}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(".")&&!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;