thuban 0.3.3 → 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 (960) hide show
  1. package/README.md +2 -0
  2. package/dist/LICENSE +21 -0
  3. package/dist/README.md +187 -0
  4. package/dist/cli.js +2 -0
  5. package/dist/packages/crucible/.golden/golden-master.json +414 -0
  6. package/dist/packages/crucible/badge-generator.js +1 -0
  7. package/dist/packages/crucible/banner.js +1 -0
  8. package/dist/packages/crucible/ci-integration.js +1 -0
  9. package/dist/packages/crucible/golden-master.js +1 -0
  10. package/dist/packages/crucible/index.js +1 -0
  11. package/dist/packages/crucible/kenny-mode.js +1 -0
  12. package/dist/packages/crucible/leaderboard.js +1 -0
  13. package/dist/packages/crucible/mutation-engine.js +1 -0
  14. package/dist/packages/crucible/pattern-learner.js +1 -0
  15. package/dist/packages/crucible/report-generator.js +1 -0
  16. package/dist/packages/crucible/rule-loader.js +1 -0
  17. package/dist/packages/crucible/seeding-engine.js +1 -0
  18. package/dist/packages/crucible/seeds/csharp/seed-001.cs +28 -0
  19. package/dist/packages/crucible/seeds/csharp/seed-002.cs +24 -0
  20. package/dist/packages/crucible/seeds/csharp/seed-003.cs +30 -0
  21. package/dist/packages/crucible/seeds/csharp/seed-004.cs +31 -0
  22. package/dist/packages/crucible/seeds/csharp/seed-005.cs +28 -0
  23. package/dist/packages/crucible/seeds/csharp/seed-006.cs +25 -0
  24. package/dist/packages/crucible/seeds/csharp/seed-007.cs +34 -0
  25. package/dist/packages/crucible/seeds/csharp/seed-008.cs +23 -0
  26. package/dist/packages/crucible/seeds/csharp/seed-009.cs +27 -0
  27. package/dist/packages/crucible/seeds/csharp/seed-010.cs +29 -0
  28. package/dist/packages/crucible/seeds/csharp/seed-011.cs +32 -0
  29. package/dist/packages/crucible/seeds/csharp/seed-012.cs +27 -0
  30. package/dist/packages/crucible/seeds/csharp/seed-013.cs +27 -0
  31. package/dist/packages/crucible/seeds/csharp/seed-014.cs +26 -0
  32. package/dist/packages/crucible/seeds/csharp/seed-015.cs +33 -0
  33. package/dist/packages/crucible/seeds/csharp/seed-016.cs +25 -0
  34. package/dist/packages/crucible/seeds/csharp/seed-017.cs +22 -0
  35. package/dist/packages/crucible/seeds/csharp/seed-018.cs +26 -0
  36. package/dist/packages/crucible/seeds/csharp/seed-019.cs +25 -0
  37. package/dist/packages/crucible/seeds/csharp/seed-020.cs +22 -0
  38. package/dist/packages/crucible/seeds/csharp/seed-021.cs +28 -0
  39. package/dist/packages/crucible/seeds/csharp/seed-022.cs +25 -0
  40. package/dist/packages/crucible/seeds/csharp/seed-023.cs +33 -0
  41. package/dist/packages/crucible/seeds/csharp/seed-024.cs +31 -0
  42. package/dist/packages/crucible/seeds/csharp/seed-025.cs +31 -0
  43. package/dist/packages/crucible/seeds/csharp/seed-026.cs +25 -0
  44. package/dist/packages/crucible/seeds/csharp/seed-027.cs +29 -0
  45. package/dist/packages/crucible/seeds/csharp/seed-028.cs +25 -0
  46. package/dist/packages/crucible/seeds/csharp/seed-029.cs +27 -0
  47. package/dist/packages/crucible/seeds/csharp/seed-030.cs +27 -0
  48. package/dist/packages/crucible/seeds/csharp/seed-031.cs +28 -0
  49. package/dist/packages/crucible/seeds/csharp/seed-032.cs +30 -0
  50. package/dist/packages/crucible/seeds/csharp/seed-033.cs +27 -0
  51. package/dist/packages/crucible/seeds/csharp/seed-034.cs +25 -0
  52. package/dist/packages/crucible/seeds/csharp/seed-035.cs +32 -0
  53. package/dist/packages/crucible/seeds/csharp/seed-036.cs +28 -0
  54. package/dist/packages/crucible/seeds/csharp/seed-037.cs +29 -0
  55. package/dist/packages/crucible/seeds/csharp/seed-038.cs +28 -0
  56. package/dist/packages/crucible/seeds/csharp/seed-039.cs +29 -0
  57. package/dist/packages/crucible/seeds/csharp/seed-040.cs +28 -0
  58. package/dist/packages/crucible/seeds/csharp/seed-041.cs +33 -0
  59. package/dist/packages/crucible/seeds/csharp/seed-042.cs +27 -0
  60. package/dist/packages/crucible/seeds/csharp/seed-043.cs +27 -0
  61. package/dist/packages/crucible/seeds/csharp/seed-044.cs +25 -0
  62. package/dist/packages/crucible/seeds/csharp/seed-045.cs +30 -0
  63. package/dist/packages/crucible/seeds/csharp/seed-046.cs +29 -0
  64. package/dist/packages/crucible/seeds/csharp/seed-047.cs +26 -0
  65. package/dist/packages/crucible/seeds/csharp/seed-048.cs +31 -0
  66. package/dist/packages/crucible/seeds/csharp/seed-049.cs +27 -0
  67. package/dist/packages/crucible/seeds/csharp/seed-050.cs +30 -0
  68. package/dist/packages/crucible/seeds/csharp/seed-051.cs +34 -0
  69. package/dist/packages/crucible/seeds/csharp/seed-052.cs +36 -0
  70. package/dist/packages/crucible/seeds/go/seed-001.go +29 -0
  71. package/dist/packages/crucible/seeds/go/seed-002.go +27 -0
  72. package/dist/packages/crucible/seeds/go/seed-003.go +31 -0
  73. package/dist/packages/crucible/seeds/go/seed-004.go +27 -0
  74. package/dist/packages/crucible/seeds/go/seed-005.go +27 -0
  75. package/dist/packages/crucible/seeds/go/seed-006.go +29 -0
  76. package/dist/packages/crucible/seeds/go/seed-007.go +24 -0
  77. package/dist/packages/crucible/seeds/go/seed-008.go +25 -0
  78. package/dist/packages/crucible/seeds/go/seed-009.go +30 -0
  79. package/dist/packages/crucible/seeds/go/seed-010.go +35 -0
  80. package/dist/packages/crucible/seeds/go/seed-011.go +24 -0
  81. package/dist/packages/crucible/seeds/go/seed-012.go +24 -0
  82. package/dist/packages/crucible/seeds/go/seed-013.go +31 -0
  83. package/dist/packages/crucible/seeds/go/seed-014.go +35 -0
  84. package/dist/packages/crucible/seeds/go/seed-015.go +25 -0
  85. package/dist/packages/crucible/seeds/go/seed-016.go +19 -0
  86. package/dist/packages/crucible/seeds/go/seed-017.go +23 -0
  87. package/dist/packages/crucible/seeds/go/seed-018.go +23 -0
  88. package/dist/packages/crucible/seeds/go/seed-019.go +22 -0
  89. package/dist/packages/crucible/seeds/go/seed-020.go +31 -0
  90. package/dist/packages/crucible/seeds/go/seed-021.go +22 -0
  91. package/dist/packages/crucible/seeds/go/seed-022.go +29 -0
  92. package/dist/packages/crucible/seeds/go/seed-023.go +26 -0
  93. package/dist/packages/crucible/seeds/go/seed-024.go +27 -0
  94. package/dist/packages/crucible/seeds/go/seed-025.go +23 -0
  95. package/dist/packages/crucible/seeds/go/seed-026.go +29 -0
  96. package/dist/packages/crucible/seeds/go/seed-027.go +26 -0
  97. package/dist/packages/crucible/seeds/go/seed-028.go +23 -0
  98. package/dist/packages/crucible/seeds/go/seed-029.go +28 -0
  99. package/dist/packages/crucible/seeds/go/seed-030.go +27 -0
  100. package/dist/packages/crucible/seeds/go/seed-031.go +28 -0
  101. package/dist/packages/crucible/seeds/go/seed-032.go +27 -0
  102. package/dist/packages/crucible/seeds/go/seed-033.go +29 -0
  103. package/dist/packages/crucible/seeds/go/seed-034.go +22 -0
  104. package/dist/packages/crucible/seeds/go/seed-035.go +32 -0
  105. package/dist/packages/crucible/seeds/go/seed-036.go +27 -0
  106. package/dist/packages/crucible/seeds/go/seed-037.go +27 -0
  107. package/dist/packages/crucible/seeds/go/seed-038.go +27 -0
  108. package/dist/packages/crucible/seeds/go/seed-039.go +26 -0
  109. package/dist/packages/crucible/seeds/go/seed-040.go +37 -0
  110. package/dist/packages/crucible/seeds/go/seed-041.go +23 -0
  111. package/dist/packages/crucible/seeds/go/seed-042.go +32 -0
  112. package/dist/packages/crucible/seeds/go/seed-043.go +35 -0
  113. package/dist/packages/crucible/seeds/go/seed-044.go +25 -0
  114. package/dist/packages/crucible/seeds/go/seed-045.go +26 -0
  115. package/dist/packages/crucible/seeds/go/seed-046.go +34 -0
  116. package/dist/packages/crucible/seeds/go/seed-047.go +26 -0
  117. package/dist/packages/crucible/seeds/go/seed-048.go +29 -0
  118. package/dist/packages/crucible/seeds/go/seed-049.go +24 -0
  119. package/dist/packages/crucible/seeds/go/seed-050.go +27 -0
  120. package/dist/packages/crucible/seeds/go/seed-051.go +38 -0
  121. package/dist/packages/crucible/seeds/go/seed-052.go +27 -0
  122. package/dist/packages/crucible/seeds/java/seed-001.java +23 -0
  123. package/dist/packages/crucible/seeds/java/seed-002.java +22 -0
  124. package/dist/packages/crucible/seeds/java/seed-003.java +28 -0
  125. package/dist/packages/crucible/seeds/java/seed-004.java +26 -0
  126. package/dist/packages/crucible/seeds/java/seed-005.java +33 -0
  127. package/dist/packages/crucible/seeds/java/seed-006.java +22 -0
  128. package/dist/packages/crucible/seeds/java/seed-007.java +21 -0
  129. package/dist/packages/crucible/seeds/java/seed-008.java +29 -0
  130. package/dist/packages/crucible/seeds/java/seed-009.java +24 -0
  131. package/dist/packages/crucible/seeds/java/seed-010.java +30 -0
  132. package/dist/packages/crucible/seeds/java/seed-011.java +25 -0
  133. package/dist/packages/crucible/seeds/java/seed-012.java +20 -0
  134. package/dist/packages/crucible/seeds/java/seed-013.java +26 -0
  135. package/dist/packages/crucible/seeds/java/seed-014.java +23 -0
  136. package/dist/packages/crucible/seeds/java/seed-015.java +25 -0
  137. package/dist/packages/crucible/seeds/java/seed-016.java +22 -0
  138. package/dist/packages/crucible/seeds/java/seed-017.java +24 -0
  139. package/dist/packages/crucible/seeds/java/seed-018.java +24 -0
  140. package/dist/packages/crucible/seeds/java/seed-019.java +38 -0
  141. package/dist/packages/crucible/seeds/java/seed-020.java +24 -0
  142. package/dist/packages/crucible/seeds/java/seed-021.java +22 -0
  143. package/dist/packages/crucible/seeds/java/seed-022.java +22 -0
  144. package/dist/packages/crucible/seeds/java/seed-023.java +22 -0
  145. package/dist/packages/crucible/seeds/java/seed-024.java +25 -0
  146. package/dist/packages/crucible/seeds/java/seed-025.java +27 -0
  147. package/dist/packages/crucible/seeds/java/seed-026.java +25 -0
  148. package/dist/packages/crucible/seeds/java/seed-027.java +22 -0
  149. package/dist/packages/crucible/seeds/java/seed-028.java +21 -0
  150. package/dist/packages/crucible/seeds/java/seed-029.java +28 -0
  151. package/dist/packages/crucible/seeds/java/seed-030.java +27 -0
  152. package/dist/packages/crucible/seeds/java/seed-031.java +23 -0
  153. package/dist/packages/crucible/seeds/java/seed-032.java +22 -0
  154. package/dist/packages/crucible/seeds/java/seed-033.java +24 -0
  155. package/dist/packages/crucible/seeds/java/seed-034.java +24 -0
  156. package/dist/packages/crucible/seeds/java/seed-035.java +29 -0
  157. package/dist/packages/crucible/seeds/java/seed-036.java +25 -0
  158. package/dist/packages/crucible/seeds/java/seed-037.java +30 -0
  159. package/dist/packages/crucible/seeds/java/seed-038.java +29 -0
  160. package/dist/packages/crucible/seeds/java/seed-039.java +26 -0
  161. package/dist/packages/crucible/seeds/java/seed-040.java +31 -0
  162. package/dist/packages/crucible/seeds/java/seed-041.java +20 -0
  163. package/dist/packages/crucible/seeds/java/seed-042.java +26 -0
  164. package/dist/packages/crucible/seeds/java/seed-043.java +31 -0
  165. package/dist/packages/crucible/seeds/java/seed-044.java +22 -0
  166. package/dist/packages/crucible/seeds/java/seed-045.java +25 -0
  167. package/dist/packages/crucible/seeds/java/seed-046.java +22 -0
  168. package/dist/packages/crucible/seeds/java/seed-047.java +26 -0
  169. package/dist/packages/crucible/seeds/java/seed-048.java +24 -0
  170. package/dist/packages/crucible/seeds/java/seed-049.java +28 -0
  171. package/dist/packages/crucible/seeds/java/seed-050.java +29 -0
  172. package/dist/packages/crucible/seeds/java/seed-051.java +25 -0
  173. package/dist/packages/crucible/seeds/java/seed-052.java +32 -0
  174. package/dist/packages/crucible/seeds/js/seed-001.js +27 -0
  175. package/dist/packages/crucible/seeds/js/seed-002.js +28 -0
  176. package/dist/packages/crucible/seeds/js/seed-003.js +30 -0
  177. package/dist/packages/crucible/seeds/js/seed-004.js +28 -0
  178. package/dist/packages/crucible/seeds/js/seed-005.js +31 -0
  179. package/dist/packages/crucible/seeds/js/seed-006.js +28 -0
  180. package/dist/packages/crucible/seeds/js/seed-007.js +26 -0
  181. package/dist/packages/crucible/seeds/js/seed-008.js +30 -0
  182. package/dist/packages/crucible/seeds/js/seed-009.js +30 -0
  183. package/dist/packages/crucible/seeds/js/seed-010.js +29 -0
  184. package/dist/packages/crucible/seeds/js/seed-011.js +34 -0
  185. package/dist/packages/crucible/seeds/js/seed-012.js +32 -0
  186. package/dist/packages/crucible/seeds/js/seed-013.js +29 -0
  187. package/dist/packages/crucible/seeds/js/seed-014.js +29 -0
  188. package/dist/packages/crucible/seeds/js/seed-015.js +31 -0
  189. package/dist/packages/crucible/seeds/js/seed-016.js +31 -0
  190. package/dist/packages/crucible/seeds/js/seed-017.js +30 -0
  191. package/dist/packages/crucible/seeds/js/seed-018.js +29 -0
  192. package/dist/packages/crucible/seeds/js/seed-019.js +32 -0
  193. package/dist/packages/crucible/seeds/js/seed-020.js +29 -0
  194. package/dist/packages/crucible/seeds/js/seed-021.js +31 -0
  195. package/dist/packages/crucible/seeds/js/seed-022.js +31 -0
  196. package/dist/packages/crucible/seeds/js/seed-023.js +29 -0
  197. package/dist/packages/crucible/seeds/js/seed-024.js +29 -0
  198. package/dist/packages/crucible/seeds/js/seed-025.js +34 -0
  199. package/dist/packages/crucible/seeds/js/seed-026.js +31 -0
  200. package/dist/packages/crucible/seeds/js/seed-027.js +31 -0
  201. package/dist/packages/crucible/seeds/js/seed-028.js +33 -0
  202. package/dist/packages/crucible/seeds/js/seed-029.js +33 -0
  203. package/dist/packages/crucible/seeds/js/seed-030.js +30 -0
  204. package/dist/packages/crucible/seeds/js/seed-031.js +31 -0
  205. package/dist/packages/crucible/seeds/js/seed-032.js +34 -0
  206. package/dist/packages/crucible/seeds/js/seed-033.js +33 -0
  207. package/dist/packages/crucible/seeds/js/seed-034.js +33 -0
  208. package/dist/packages/crucible/seeds/js/seed-035.js +30 -0
  209. package/dist/packages/crucible/seeds/js/seed-036.js +30 -0
  210. package/dist/packages/crucible/seeds/js/seed-037.js +29 -0
  211. package/dist/packages/crucible/seeds/js/seed-038.js +31 -0
  212. package/dist/packages/crucible/seeds/js/seed-039.js +34 -0
  213. package/dist/packages/crucible/seeds/js/seed-040.js +35 -0
  214. package/dist/packages/crucible/seeds/js/seed-041.js +32 -0
  215. package/dist/packages/crucible/seeds/js/seed-042.js +32 -0
  216. package/dist/packages/crucible/seeds/js/seed-043.js +27 -0
  217. package/dist/packages/crucible/seeds/js/seed-044.js +29 -0
  218. package/dist/packages/crucible/seeds/js/seed-045.js +33 -0
  219. package/dist/packages/crucible/seeds/js/seed-046.js +33 -0
  220. package/dist/packages/crucible/seeds/js/seed-047.js +29 -0
  221. package/dist/packages/crucible/seeds/js/seed-048.js +29 -0
  222. package/dist/packages/crucible/seeds/js/seed-049.js +34 -0
  223. package/dist/packages/crucible/seeds/js/seed-050.js +34 -0
  224. package/dist/packages/crucible/seeds/js/seed-051.js +30 -0
  225. package/dist/packages/crucible/seeds/js/seed-052.js +30 -0
  226. package/dist/packages/crucible/seeds/js/seed-053.js +26 -0
  227. package/dist/packages/crucible/seeds/js/seed-054.js +35 -0
  228. package/dist/packages/crucible/seeds/js/seed-055.js +33 -0
  229. package/dist/packages/crucible/seeds/kotlin/seed-001.kt +24 -0
  230. package/dist/packages/crucible/seeds/kotlin/seed-002.kt +27 -0
  231. package/dist/packages/crucible/seeds/kotlin/seed-003.kt +29 -0
  232. package/dist/packages/crucible/seeds/kotlin/seed-004.kt +24 -0
  233. package/dist/packages/crucible/seeds/kotlin/seed-005.kt +29 -0
  234. package/dist/packages/crucible/seeds/kotlin/seed-006.kt +21 -0
  235. package/dist/packages/crucible/seeds/kotlin/seed-007.kt +23 -0
  236. package/dist/packages/crucible/seeds/kotlin/seed-008.kt +25 -0
  237. package/dist/packages/crucible/seeds/kotlin/seed-009.kt +21 -0
  238. package/dist/packages/crucible/seeds/kotlin/seed-010.kt +28 -0
  239. package/dist/packages/crucible/seeds/kotlin/seed-011.kt +22 -0
  240. package/dist/packages/crucible/seeds/kotlin/seed-012.kt +25 -0
  241. package/dist/packages/crucible/seeds/kotlin/seed-013.kt +30 -0
  242. package/dist/packages/crucible/seeds/kotlin/seed-014.kt +27 -0
  243. package/dist/packages/crucible/seeds/kotlin/seed-015.kt +24 -0
  244. package/dist/packages/crucible/seeds/kotlin/seed-016.kt +28 -0
  245. package/dist/packages/crucible/seeds/kotlin/seed-017.kt +33 -0
  246. package/dist/packages/crucible/seeds/kotlin/seed-018.kt +31 -0
  247. package/dist/packages/crucible/seeds/kotlin/seed-019.kt +28 -0
  248. package/dist/packages/crucible/seeds/kotlin/seed-020.kt +31 -0
  249. package/dist/packages/crucible/seeds/kotlin/seed-021.kt +34 -0
  250. package/dist/packages/crucible/seeds/kotlin/seed-022.kt +29 -0
  251. package/dist/packages/crucible/seeds/kotlin/seed-023.kt +20 -0
  252. package/dist/packages/crucible/seeds/kotlin/seed-024.kt +23 -0
  253. package/dist/packages/crucible/seeds/kotlin/seed-025.kt +23 -0
  254. package/dist/packages/crucible/seeds/kotlin/seed-026.kt +26 -0
  255. package/dist/packages/crucible/seeds/kotlin/seed-027.kt +29 -0
  256. package/dist/packages/crucible/seeds/kotlin/seed-028.kt +24 -0
  257. package/dist/packages/crucible/seeds/kotlin/seed-029.kt +27 -0
  258. package/dist/packages/crucible/seeds/kotlin/seed-030.kt +26 -0
  259. package/dist/packages/crucible/seeds/kotlin/seed-031.kt +28 -0
  260. package/dist/packages/crucible/seeds/kotlin/seed-032.kt +29 -0
  261. package/dist/packages/crucible/seeds/kotlin/seed-033.kt +30 -0
  262. package/dist/packages/crucible/seeds/kotlin/seed-034.kt +26 -0
  263. package/dist/packages/crucible/seeds/kotlin/seed-035.kt +33 -0
  264. package/dist/packages/crucible/seeds/kotlin/seed-036.kt +25 -0
  265. package/dist/packages/crucible/seeds/kotlin/seed-037.kt +26 -0
  266. package/dist/packages/crucible/seeds/kotlin/seed-038.kt +25 -0
  267. package/dist/packages/crucible/seeds/kotlin/seed-039.kt +29 -0
  268. package/dist/packages/crucible/seeds/kotlin/seed-040.kt +28 -0
  269. package/dist/packages/crucible/seeds/kotlin/seed-041.kt +31 -0
  270. package/dist/packages/crucible/seeds/kotlin/seed-042.kt +27 -0
  271. package/dist/packages/crucible/seeds/kotlin/seed-043.kt +26 -0
  272. package/dist/packages/crucible/seeds/kotlin/seed-044.kt +27 -0
  273. package/dist/packages/crucible/seeds/kotlin/seed-045.kt +27 -0
  274. package/dist/packages/crucible/seeds/kotlin/seed-046.kt +33 -0
  275. package/dist/packages/crucible/seeds/kotlin/seed-047.kt +32 -0
  276. package/dist/packages/crucible/seeds/kotlin/seed-048.kt +20 -0
  277. package/dist/packages/crucible/seeds/kotlin/seed-049.kt +23 -0
  278. package/dist/packages/crucible/seeds/kotlin/seed-050.kt +19 -0
  279. package/dist/packages/crucible/seeds/php/seed-001.php +23 -0
  280. package/dist/packages/crucible/seeds/php/seed-002.php +18 -0
  281. package/dist/packages/crucible/seeds/php/seed-003.php +25 -0
  282. package/dist/packages/crucible/seeds/php/seed-004.php +21 -0
  283. package/dist/packages/crucible/seeds/php/seed-005.php +21 -0
  284. package/dist/packages/crucible/seeds/php/seed-006.php +21 -0
  285. package/dist/packages/crucible/seeds/php/seed-007.php +18 -0
  286. package/dist/packages/crucible/seeds/php/seed-008.php +19 -0
  287. package/dist/packages/crucible/seeds/php/seed-009.php +24 -0
  288. package/dist/packages/crucible/seeds/php/seed-010.php +24 -0
  289. package/dist/packages/crucible/seeds/php/seed-011.php +18 -0
  290. package/dist/packages/crucible/seeds/php/seed-012.php +24 -0
  291. package/dist/packages/crucible/seeds/php/seed-013.php +23 -0
  292. package/dist/packages/crucible/seeds/php/seed-014.php +20 -0
  293. package/dist/packages/crucible/seeds/php/seed-015.php +18 -0
  294. package/dist/packages/crucible/seeds/php/seed-016.php +18 -0
  295. package/dist/packages/crucible/seeds/php/seed-017.php +18 -0
  296. package/dist/packages/crucible/seeds/php/seed-018.php +17 -0
  297. package/dist/packages/crucible/seeds/php/seed-019.php +24 -0
  298. package/dist/packages/crucible/seeds/php/seed-020.php +19 -0
  299. package/dist/packages/crucible/seeds/php/seed-021.php +22 -0
  300. package/dist/packages/crucible/seeds/php/seed-022.php +15 -0
  301. package/dist/packages/crucible/seeds/php/seed-023.php +24 -0
  302. package/dist/packages/crucible/seeds/php/seed-024.php +22 -0
  303. package/dist/packages/crucible/seeds/php/seed-025.php +20 -0
  304. package/dist/packages/crucible/seeds/php/seed-026.php +22 -0
  305. package/dist/packages/crucible/seeds/php/seed-027.php +15 -0
  306. package/dist/packages/crucible/seeds/php/seed-028.php +15 -0
  307. package/dist/packages/crucible/seeds/php/seed-029.php +21 -0
  308. package/dist/packages/crucible/seeds/php/seed-030.php +21 -0
  309. package/dist/packages/crucible/seeds/php/seed-031.php +23 -0
  310. package/dist/packages/crucible/seeds/php/seed-032.php +18 -0
  311. package/dist/packages/crucible/seeds/php/seed-033.php +18 -0
  312. package/dist/packages/crucible/seeds/php/seed-034.php +24 -0
  313. package/dist/packages/crucible/seeds/php/seed-035.php +22 -0
  314. package/dist/packages/crucible/seeds/php/seed-036.php +17 -0
  315. package/dist/packages/crucible/seeds/php/seed-037.php +19 -0
  316. package/dist/packages/crucible/seeds/php/seed-038.php +20 -0
  317. package/dist/packages/crucible/seeds/php/seed-039.php +15 -0
  318. package/dist/packages/crucible/seeds/php/seed-040.php +21 -0
  319. package/dist/packages/crucible/seeds/php/seed-041.php +20 -0
  320. package/dist/packages/crucible/seeds/php/seed-042.php +21 -0
  321. package/dist/packages/crucible/seeds/php/seed-043.php +21 -0
  322. package/dist/packages/crucible/seeds/php/seed-044.php +25 -0
  323. package/dist/packages/crucible/seeds/php/seed-045.php +25 -0
  324. package/dist/packages/crucible/seeds/php/seed-046.php +20 -0
  325. package/dist/packages/crucible/seeds/php/seed-047.php +21 -0
  326. package/dist/packages/crucible/seeds/php/seed-048.php +19 -0
  327. package/dist/packages/crucible/seeds/php/seed-049.php +18 -0
  328. package/dist/packages/crucible/seeds/php/seed-050.php +20 -0
  329. package/dist/packages/crucible/seeds/python/seed-001.py +33 -0
  330. package/dist/packages/crucible/seeds/python/seed-002.py +39 -0
  331. package/dist/packages/crucible/seeds/python/seed-003.py +31 -0
  332. package/dist/packages/crucible/seeds/python/seed-004.py +33 -0
  333. package/dist/packages/crucible/seeds/python/seed-005.py +33 -0
  334. package/dist/packages/crucible/seeds/python/seed-006.py +34 -0
  335. package/dist/packages/crucible/seeds/python/seed-007.py +33 -0
  336. package/dist/packages/crucible/seeds/python/seed-008.py +38 -0
  337. package/dist/packages/crucible/seeds/python/seed-009.py +38 -0
  338. package/dist/packages/crucible/seeds/python/seed-010.py +42 -0
  339. package/dist/packages/crucible/seeds/python/seed-011.py +30 -0
  340. package/dist/packages/crucible/seeds/python/seed-012.py +35 -0
  341. package/dist/packages/crucible/seeds/python/seed-013.py +33 -0
  342. package/dist/packages/crucible/seeds/python/seed-014.py +40 -0
  343. package/dist/packages/crucible/seeds/python/seed-015.py +33 -0
  344. package/dist/packages/crucible/seeds/python/seed-016.py +34 -0
  345. package/dist/packages/crucible/seeds/python/seed-017.py +34 -0
  346. package/dist/packages/crucible/seeds/python/seed-018.py +30 -0
  347. package/dist/packages/crucible/seeds/python/seed-019.py +33 -0
  348. package/dist/packages/crucible/seeds/python/seed-020.py +37 -0
  349. package/dist/packages/crucible/seeds/python/seed-021 2.py +37 -0
  350. package/dist/packages/crucible/seeds/python/seed-021 3.py +37 -0
  351. package/dist/packages/crucible/seeds/python/seed-021.py +37 -0
  352. package/dist/packages/crucible/seeds/python/seed-022 2.py +34 -0
  353. package/dist/packages/crucible/seeds/python/seed-022 3.py +34 -0
  354. package/dist/packages/crucible/seeds/python/seed-022.py +34 -0
  355. package/dist/packages/crucible/seeds/python/seed-023 2.py +37 -0
  356. package/dist/packages/crucible/seeds/python/seed-023 3.py +37 -0
  357. package/dist/packages/crucible/seeds/python/seed-023.py +37 -0
  358. package/dist/packages/crucible/seeds/python/seed-024 2.py +38 -0
  359. package/dist/packages/crucible/seeds/python/seed-024 3.py +38 -0
  360. package/dist/packages/crucible/seeds/python/seed-024.py +38 -0
  361. package/dist/packages/crucible/seeds/python/seed-025 2.py +40 -0
  362. package/dist/packages/crucible/seeds/python/seed-025 3.py +40 -0
  363. package/dist/packages/crucible/seeds/python/seed-025.py +40 -0
  364. package/dist/packages/crucible/seeds/python/seed-026 2.py +35 -0
  365. package/dist/packages/crucible/seeds/python/seed-026 3.py +35 -0
  366. package/dist/packages/crucible/seeds/python/seed-026.py +35 -0
  367. package/dist/packages/crucible/seeds/python/seed-027 2.py +35 -0
  368. package/dist/packages/crucible/seeds/python/seed-027 3.py +35 -0
  369. package/dist/packages/crucible/seeds/python/seed-027.py +35 -0
  370. package/dist/packages/crucible/seeds/python/seed-028 2.py +42 -0
  371. package/dist/packages/crucible/seeds/python/seed-028 3.py +42 -0
  372. package/dist/packages/crucible/seeds/python/seed-028.py +42 -0
  373. package/dist/packages/crucible/seeds/python/seed-029.py +42 -0
  374. package/dist/packages/crucible/seeds/python/seed-030 2.py +37 -0
  375. package/dist/packages/crucible/seeds/python/seed-030 3.py +37 -0
  376. package/dist/packages/crucible/seeds/python/seed-030.py +37 -0
  377. package/dist/packages/crucible/seeds/python/seed-031 2.py +34 -0
  378. package/dist/packages/crucible/seeds/python/seed-031 3.py +34 -0
  379. package/dist/packages/crucible/seeds/python/seed-031.py +34 -0
  380. package/dist/packages/crucible/seeds/python/seed-032.py +33 -0
  381. package/dist/packages/crucible/seeds/python/seed-033.py +32 -0
  382. package/dist/packages/crucible/seeds/python/seed-034.py +38 -0
  383. package/dist/packages/crucible/seeds/python/seed-035.py +35 -0
  384. package/dist/packages/crucible/seeds/python/seed-036 2.py +33 -0
  385. package/dist/packages/crucible/seeds/python/seed-036 3.py +33 -0
  386. package/dist/packages/crucible/seeds/python/seed-036.py +33 -0
  387. package/dist/packages/crucible/seeds/python/seed-037 2.py +41 -0
  388. package/dist/packages/crucible/seeds/python/seed-037 3.py +41 -0
  389. package/dist/packages/crucible/seeds/python/seed-037.py +41 -0
  390. package/dist/packages/crucible/seeds/python/seed-038 2.py +33 -0
  391. package/dist/packages/crucible/seeds/python/seed-038 3.py +33 -0
  392. package/dist/packages/crucible/seeds/python/seed-038.py +33 -0
  393. package/dist/packages/crucible/seeds/python/seed-039 2.py +39 -0
  394. package/dist/packages/crucible/seeds/python/seed-039 3.py +39 -0
  395. package/dist/packages/crucible/seeds/python/seed-039.py +39 -0
  396. package/dist/packages/crucible/seeds/python/seed-040 2.py +39 -0
  397. package/dist/packages/crucible/seeds/python/seed-040 3.py +39 -0
  398. package/dist/packages/crucible/seeds/python/seed-040.py +39 -0
  399. package/dist/packages/crucible/seeds/python/seed-041 2.py +37 -0
  400. package/dist/packages/crucible/seeds/python/seed-041 3.py +37 -0
  401. package/dist/packages/crucible/seeds/python/seed-041.py +37 -0
  402. package/dist/packages/crucible/seeds/python/seed-042 2.py +38 -0
  403. package/dist/packages/crucible/seeds/python/seed-042 3.py +38 -0
  404. package/dist/packages/crucible/seeds/python/seed-042.py +38 -0
  405. package/dist/packages/crucible/seeds/python/seed-043 2.py +32 -0
  406. package/dist/packages/crucible/seeds/python/seed-043 3.py +32 -0
  407. package/dist/packages/crucible/seeds/python/seed-043.py +32 -0
  408. package/dist/packages/crucible/seeds/python/seed-044 2.py +38 -0
  409. package/dist/packages/crucible/seeds/python/seed-044 3.py +38 -0
  410. package/dist/packages/crucible/seeds/python/seed-044.py +38 -0
  411. package/dist/packages/crucible/seeds/python/seed-045 2.py +36 -0
  412. package/dist/packages/crucible/seeds/python/seed-045 3.py +36 -0
  413. package/dist/packages/crucible/seeds/python/seed-045.py +36 -0
  414. package/dist/packages/crucible/seeds/python/seed-046 2.py +33 -0
  415. package/dist/packages/crucible/seeds/python/seed-046 3.py +33 -0
  416. package/dist/packages/crucible/seeds/python/seed-046.py +33 -0
  417. package/dist/packages/crucible/seeds/python/seed-047 2.py +44 -0
  418. package/dist/packages/crucible/seeds/python/seed-047 3.py +44 -0
  419. package/dist/packages/crucible/seeds/python/seed-047.py +44 -0
  420. package/dist/packages/crucible/seeds/python/seed-048 2.py +35 -0
  421. package/dist/packages/crucible/seeds/python/seed-048 3.py +35 -0
  422. package/dist/packages/crucible/seeds/python/seed-048.py +35 -0
  423. package/dist/packages/crucible/seeds/python/seed-049 2.py +39 -0
  424. package/dist/packages/crucible/seeds/python/seed-049 3.py +39 -0
  425. package/dist/packages/crucible/seeds/python/seed-049.py +39 -0
  426. package/dist/packages/crucible/seeds/python/seed-050 2.py +39 -0
  427. package/dist/packages/crucible/seeds/python/seed-050 3.py +39 -0
  428. package/dist/packages/crucible/seeds/python/seed-050.py +39 -0
  429. package/dist/packages/crucible/seeds/python/seed-051 2.py +38 -0
  430. package/dist/packages/crucible/seeds/python/seed-051 3.py +38 -0
  431. package/dist/packages/crucible/seeds/python/seed-051.py +38 -0
  432. package/dist/packages/crucible/seeds/python/seed-052 2.py +41 -0
  433. package/dist/packages/crucible/seeds/python/seed-052 3.py +41 -0
  434. package/dist/packages/crucible/seeds/python/seed-052.py +41 -0
  435. package/dist/packages/crucible/seeds/ruby/seed-001 2.rb +15 -0
  436. package/dist/packages/crucible/seeds/ruby/seed-001 3.rb +15 -0
  437. package/dist/packages/crucible/seeds/ruby/seed-001.rb +15 -0
  438. package/dist/packages/crucible/seeds/ruby/seed-002 2.rb +22 -0
  439. package/dist/packages/crucible/seeds/ruby/seed-002 3.rb +22 -0
  440. package/dist/packages/crucible/seeds/ruby/seed-002.rb +22 -0
  441. package/dist/packages/crucible/seeds/ruby/seed-003 2.rb +25 -0
  442. package/dist/packages/crucible/seeds/ruby/seed-003 3.rb +25 -0
  443. package/dist/packages/crucible/seeds/ruby/seed-003.rb +25 -0
  444. package/dist/packages/crucible/seeds/ruby/seed-004 2.rb +17 -0
  445. package/dist/packages/crucible/seeds/ruby/seed-004 3.rb +17 -0
  446. package/dist/packages/crucible/seeds/ruby/seed-004.rb +17 -0
  447. package/dist/packages/crucible/seeds/ruby/seed-005 2.rb +21 -0
  448. package/dist/packages/crucible/seeds/ruby/seed-005 3.rb +21 -0
  449. package/dist/packages/crucible/seeds/ruby/seed-005.rb +21 -0
  450. package/dist/packages/crucible/seeds/ruby/seed-006 2.rb +17 -0
  451. package/dist/packages/crucible/seeds/ruby/seed-006 3.rb +17 -0
  452. package/dist/packages/crucible/seeds/ruby/seed-006.rb +17 -0
  453. package/dist/packages/crucible/seeds/ruby/seed-007 2.rb +16 -0
  454. package/dist/packages/crucible/seeds/ruby/seed-007 3.rb +16 -0
  455. package/dist/packages/crucible/seeds/ruby/seed-007.rb +16 -0
  456. package/dist/packages/crucible/seeds/ruby/seed-008 2.rb +18 -0
  457. package/dist/packages/crucible/seeds/ruby/seed-008 3.rb +18 -0
  458. package/dist/packages/crucible/seeds/ruby/seed-008.rb +18 -0
  459. package/dist/packages/crucible/seeds/ruby/seed-009 2.rb +20 -0
  460. package/dist/packages/crucible/seeds/ruby/seed-009 3.rb +20 -0
  461. package/dist/packages/crucible/seeds/ruby/seed-009.rb +20 -0
  462. package/dist/packages/crucible/seeds/ruby/seed-010 2.rb +24 -0
  463. package/dist/packages/crucible/seeds/ruby/seed-010 3.rb +24 -0
  464. package/dist/packages/crucible/seeds/ruby/seed-010.rb +24 -0
  465. package/dist/packages/crucible/seeds/ruby/seed-011 2.rb +21 -0
  466. package/dist/packages/crucible/seeds/ruby/seed-011 3.rb +21 -0
  467. package/dist/packages/crucible/seeds/ruby/seed-011.rb +21 -0
  468. package/dist/packages/crucible/seeds/ruby/seed-012 2.rb +22 -0
  469. package/dist/packages/crucible/seeds/ruby/seed-012 3.rb +22 -0
  470. package/dist/packages/crucible/seeds/ruby/seed-012.rb +22 -0
  471. package/dist/packages/crucible/seeds/ruby/seed-013 2.rb +21 -0
  472. package/dist/packages/crucible/seeds/ruby/seed-013 3.rb +21 -0
  473. package/dist/packages/crucible/seeds/ruby/seed-013.rb +21 -0
  474. package/dist/packages/crucible/seeds/ruby/seed-014 2.rb +16 -0
  475. package/dist/packages/crucible/seeds/ruby/seed-014 3.rb +16 -0
  476. package/dist/packages/crucible/seeds/ruby/seed-014.rb +16 -0
  477. package/dist/packages/crucible/seeds/ruby/seed-015 2.rb +18 -0
  478. package/dist/packages/crucible/seeds/ruby/seed-015 3.rb +18 -0
  479. package/dist/packages/crucible/seeds/ruby/seed-015.rb +18 -0
  480. package/dist/packages/crucible/seeds/ruby/seed-016 2.rb +17 -0
  481. package/dist/packages/crucible/seeds/ruby/seed-016 3.rb +17 -0
  482. package/dist/packages/crucible/seeds/ruby/seed-016.rb +17 -0
  483. package/dist/packages/crucible/seeds/ruby/seed-017 2.rb +25 -0
  484. package/dist/packages/crucible/seeds/ruby/seed-017 3.rb +25 -0
  485. package/dist/packages/crucible/seeds/ruby/seed-017.rb +25 -0
  486. package/dist/packages/crucible/seeds/ruby/seed-018 2.rb +23 -0
  487. package/dist/packages/crucible/seeds/ruby/seed-018 3.rb +23 -0
  488. package/dist/packages/crucible/seeds/ruby/seed-018.rb +23 -0
  489. package/dist/packages/crucible/seeds/ruby/seed-019 2.rb +20 -0
  490. package/dist/packages/crucible/seeds/ruby/seed-019 3.rb +20 -0
  491. package/dist/packages/crucible/seeds/ruby/seed-019.rb +20 -0
  492. package/dist/packages/crucible/seeds/ruby/seed-020 2.rb +17 -0
  493. package/dist/packages/crucible/seeds/ruby/seed-020 3.rb +17 -0
  494. package/dist/packages/crucible/seeds/ruby/seed-020.rb +17 -0
  495. package/dist/packages/crucible/seeds/ruby/seed-021 2.rb +20 -0
  496. package/dist/packages/crucible/seeds/ruby/seed-021 3.rb +20 -0
  497. package/dist/packages/crucible/seeds/ruby/seed-021.rb +20 -0
  498. package/dist/packages/crucible/seeds/ruby/seed-022 2.rb +21 -0
  499. package/dist/packages/crucible/seeds/ruby/seed-022 3.rb +21 -0
  500. package/dist/packages/crucible/seeds/ruby/seed-022.rb +21 -0
  501. package/dist/packages/crucible/seeds/ruby/seed-023 2.rb +19 -0
  502. package/dist/packages/crucible/seeds/ruby/seed-023 3.rb +19 -0
  503. package/dist/packages/crucible/seeds/ruby/seed-023.rb +19 -0
  504. package/dist/packages/crucible/seeds/ruby/seed-024 2.rb +17 -0
  505. package/dist/packages/crucible/seeds/ruby/seed-024 3.rb +17 -0
  506. package/dist/packages/crucible/seeds/ruby/seed-024.rb +17 -0
  507. package/dist/packages/crucible/seeds/ruby/seed-025 2.rb +17 -0
  508. package/dist/packages/crucible/seeds/ruby/seed-025 3.rb +17 -0
  509. package/dist/packages/crucible/seeds/ruby/seed-025.rb +17 -0
  510. package/dist/packages/crucible/seeds/ruby/seed-026 2.rb +18 -0
  511. package/dist/packages/crucible/seeds/ruby/seed-026 3.rb +18 -0
  512. package/dist/packages/crucible/seeds/ruby/seed-026.rb +18 -0
  513. package/dist/packages/crucible/seeds/ruby/seed-027 2.rb +21 -0
  514. package/dist/packages/crucible/seeds/ruby/seed-027 3.rb +21 -0
  515. package/dist/packages/crucible/seeds/ruby/seed-027.rb +21 -0
  516. package/dist/packages/crucible/seeds/ruby/seed-028 2.rb +22 -0
  517. package/dist/packages/crucible/seeds/ruby/seed-028 3.rb +22 -0
  518. package/dist/packages/crucible/seeds/ruby/seed-028.rb +22 -0
  519. package/dist/packages/crucible/seeds/ruby/seed-029 2.rb +19 -0
  520. package/dist/packages/crucible/seeds/ruby/seed-029 3.rb +19 -0
  521. package/dist/packages/crucible/seeds/ruby/seed-029.rb +19 -0
  522. package/dist/packages/crucible/seeds/ruby/seed-030 2.rb +20 -0
  523. package/dist/packages/crucible/seeds/ruby/seed-030 3.rb +20 -0
  524. package/dist/packages/crucible/seeds/ruby/seed-030.rb +20 -0
  525. package/dist/packages/crucible/seeds/ruby/seed-031 2.rb +17 -0
  526. package/dist/packages/crucible/seeds/ruby/seed-031 3.rb +17 -0
  527. package/dist/packages/crucible/seeds/ruby/seed-031.rb +17 -0
  528. package/dist/packages/crucible/seeds/ruby/seed-032 2.rb +22 -0
  529. package/dist/packages/crucible/seeds/ruby/seed-032 3.rb +22 -0
  530. package/dist/packages/crucible/seeds/ruby/seed-032.rb +22 -0
  531. package/dist/packages/crucible/seeds/ruby/seed-033 2.rb +18 -0
  532. package/dist/packages/crucible/seeds/ruby/seed-033 3.rb +18 -0
  533. package/dist/packages/crucible/seeds/ruby/seed-033.rb +18 -0
  534. package/dist/packages/crucible/seeds/ruby/seed-034 2.rb +19 -0
  535. package/dist/packages/crucible/seeds/ruby/seed-034 3.rb +19 -0
  536. package/dist/packages/crucible/seeds/ruby/seed-034.rb +19 -0
  537. package/dist/packages/crucible/seeds/ruby/seed-035 2.rb +19 -0
  538. package/dist/packages/crucible/seeds/ruby/seed-035 3.rb +19 -0
  539. package/dist/packages/crucible/seeds/ruby/seed-035.rb +19 -0
  540. package/dist/packages/crucible/seeds/ruby/seed-036 2.rb +18 -0
  541. package/dist/packages/crucible/seeds/ruby/seed-036 3.rb +18 -0
  542. package/dist/packages/crucible/seeds/ruby/seed-036.rb +18 -0
  543. package/dist/packages/crucible/seeds/ruby/seed-037 2.rb +21 -0
  544. package/dist/packages/crucible/seeds/ruby/seed-037 3.rb +21 -0
  545. package/dist/packages/crucible/seeds/ruby/seed-037.rb +21 -0
  546. package/dist/packages/crucible/seeds/ruby/seed-038 2.rb +24 -0
  547. package/dist/packages/crucible/seeds/ruby/seed-038 3.rb +24 -0
  548. package/dist/packages/crucible/seeds/ruby/seed-038.rb +24 -0
  549. package/dist/packages/crucible/seeds/ruby/seed-039 2.rb +24 -0
  550. package/dist/packages/crucible/seeds/ruby/seed-039 3.rb +24 -0
  551. package/dist/packages/crucible/seeds/ruby/seed-039.rb +24 -0
  552. package/dist/packages/crucible/seeds/ruby/seed-040 2.rb +22 -0
  553. package/dist/packages/crucible/seeds/ruby/seed-040 3.rb +22 -0
  554. package/dist/packages/crucible/seeds/ruby/seed-040.rb +22 -0
  555. package/dist/packages/crucible/seeds/ruby/seed-041 2.rb +23 -0
  556. package/dist/packages/crucible/seeds/ruby/seed-041 3.rb +23 -0
  557. package/dist/packages/crucible/seeds/ruby/seed-041.rb +23 -0
  558. package/dist/packages/crucible/seeds/ruby/seed-042 2.rb +25 -0
  559. package/dist/packages/crucible/seeds/ruby/seed-042 3.rb +25 -0
  560. package/dist/packages/crucible/seeds/ruby/seed-042.rb +25 -0
  561. package/dist/packages/crucible/seeds/ruby/seed-043 2.rb +23 -0
  562. package/dist/packages/crucible/seeds/ruby/seed-043 3.rb +23 -0
  563. package/dist/packages/crucible/seeds/ruby/seed-043.rb +23 -0
  564. package/dist/packages/crucible/seeds/ruby/seed-044 2.rb +16 -0
  565. package/dist/packages/crucible/seeds/ruby/seed-044 3.rb +16 -0
  566. package/dist/packages/crucible/seeds/ruby/seed-044.rb +16 -0
  567. package/dist/packages/crucible/seeds/ruby/seed-045 2.rb +22 -0
  568. package/dist/packages/crucible/seeds/ruby/seed-045 3.rb +22 -0
  569. package/dist/packages/crucible/seeds/ruby/seed-045.rb +22 -0
  570. package/dist/packages/crucible/seeds/ruby/seed-046 2.rb +27 -0
  571. package/dist/packages/crucible/seeds/ruby/seed-046 3.rb +27 -0
  572. package/dist/packages/crucible/seeds/ruby/seed-046.rb +27 -0
  573. package/dist/packages/crucible/seeds/ruby/seed-047 2.rb +26 -0
  574. package/dist/packages/crucible/seeds/ruby/seed-047 3.rb +26 -0
  575. package/dist/packages/crucible/seeds/ruby/seed-047.rb +26 -0
  576. package/dist/packages/crucible/seeds/ruby/seed-048 2.rb +24 -0
  577. package/dist/packages/crucible/seeds/ruby/seed-048 3.rb +24 -0
  578. package/dist/packages/crucible/seeds/ruby/seed-048.rb +24 -0
  579. package/dist/packages/crucible/seeds/ruby/seed-049 2.rb +20 -0
  580. package/dist/packages/crucible/seeds/ruby/seed-049 3.rb +20 -0
  581. package/dist/packages/crucible/seeds/ruby/seed-049.rb +20 -0
  582. package/dist/packages/crucible/seeds/ruby/seed-050 2.rb +27 -0
  583. package/dist/packages/crucible/seeds/ruby/seed-050 3.rb +27 -0
  584. package/dist/packages/crucible/seeds/ruby/seed-050.rb +27 -0
  585. package/dist/packages/crucible/seeds/rust/seed-001 2.rs +25 -0
  586. package/dist/packages/crucible/seeds/rust/seed-001 3.rs +25 -0
  587. package/dist/packages/crucible/seeds/rust/seed-001.rs +25 -0
  588. package/dist/packages/crucible/seeds/rust/seed-002 2.rs +20 -0
  589. package/dist/packages/crucible/seeds/rust/seed-002 3.rs +20 -0
  590. package/dist/packages/crucible/seeds/rust/seed-002.rs +20 -0
  591. package/dist/packages/crucible/seeds/rust/seed-003 2.rs +23 -0
  592. package/dist/packages/crucible/seeds/rust/seed-003 3.rs +23 -0
  593. package/dist/packages/crucible/seeds/rust/seed-003.rs +23 -0
  594. package/dist/packages/crucible/seeds/rust/seed-004 2.rs +21 -0
  595. package/dist/packages/crucible/seeds/rust/seed-004 3.rs +21 -0
  596. package/dist/packages/crucible/seeds/rust/seed-004.rs +21 -0
  597. package/dist/packages/crucible/seeds/rust/seed-005 2.rs +21 -0
  598. package/dist/packages/crucible/seeds/rust/seed-005 3.rs +21 -0
  599. package/dist/packages/crucible/seeds/rust/seed-005.rs +21 -0
  600. package/dist/packages/crucible/seeds/rust/seed-006 2.rs +24 -0
  601. package/dist/packages/crucible/seeds/rust/seed-006 3.rs +24 -0
  602. package/dist/packages/crucible/seeds/rust/seed-006.rs +24 -0
  603. package/dist/packages/crucible/seeds/rust/seed-007 2.rs +20 -0
  604. package/dist/packages/crucible/seeds/rust/seed-007 3.rs +20 -0
  605. package/dist/packages/crucible/seeds/rust/seed-007.rs +20 -0
  606. package/dist/packages/crucible/seeds/rust/seed-008 2.rs +19 -0
  607. package/dist/packages/crucible/seeds/rust/seed-008 3.rs +19 -0
  608. package/dist/packages/crucible/seeds/rust/seed-008.rs +19 -0
  609. package/dist/packages/crucible/seeds/rust/seed-009 2.rs +28 -0
  610. package/dist/packages/crucible/seeds/rust/seed-009 3.rs +28 -0
  611. package/dist/packages/crucible/seeds/rust/seed-009.rs +28 -0
  612. package/dist/packages/crucible/seeds/rust/seed-010 2.rs +28 -0
  613. package/dist/packages/crucible/seeds/rust/seed-010 3.rs +28 -0
  614. package/dist/packages/crucible/seeds/rust/seed-010.rs +28 -0
  615. package/dist/packages/crucible/seeds/rust/seed-011 2.rs +25 -0
  616. package/dist/packages/crucible/seeds/rust/seed-011 3.rs +25 -0
  617. package/dist/packages/crucible/seeds/rust/seed-011.rs +25 -0
  618. package/dist/packages/crucible/seeds/rust/seed-012 2.rs +31 -0
  619. package/dist/packages/crucible/seeds/rust/seed-012 3.rs +31 -0
  620. package/dist/packages/crucible/seeds/rust/seed-012.rs +31 -0
  621. package/dist/packages/crucible/seeds/rust/seed-013 2.rs +27 -0
  622. package/dist/packages/crucible/seeds/rust/seed-013 3.rs +27 -0
  623. package/dist/packages/crucible/seeds/rust/seed-013.rs +27 -0
  624. package/dist/packages/crucible/seeds/rust/seed-014 2.rs +30 -0
  625. package/dist/packages/crucible/seeds/rust/seed-014 3.rs +30 -0
  626. package/dist/packages/crucible/seeds/rust/seed-014.rs +30 -0
  627. package/dist/packages/crucible/seeds/rust/seed-015 2.rs +33 -0
  628. package/dist/packages/crucible/seeds/rust/seed-015 3.rs +33 -0
  629. package/dist/packages/crucible/seeds/rust/seed-015.rs +33 -0
  630. package/dist/packages/crucible/seeds/rust/seed-016 2.rs +22 -0
  631. package/dist/packages/crucible/seeds/rust/seed-016 3.rs +22 -0
  632. package/dist/packages/crucible/seeds/rust/seed-016.rs +22 -0
  633. package/dist/packages/crucible/seeds/rust/seed-017 2.rs +28 -0
  634. package/dist/packages/crucible/seeds/rust/seed-017 3.rs +28 -0
  635. package/dist/packages/crucible/seeds/rust/seed-017.rs +28 -0
  636. package/dist/packages/crucible/seeds/rust/seed-018 2.rs +21 -0
  637. package/dist/packages/crucible/seeds/rust/seed-018 3.rs +21 -0
  638. package/dist/packages/crucible/seeds/rust/seed-018.rs +21 -0
  639. package/dist/packages/crucible/seeds/rust/seed-019 2.rs +36 -0
  640. package/dist/packages/crucible/seeds/rust/seed-019 3.rs +36 -0
  641. package/dist/packages/crucible/seeds/rust/seed-019.rs +36 -0
  642. package/dist/packages/crucible/seeds/rust/seed-020 2.rs +27 -0
  643. package/dist/packages/crucible/seeds/rust/seed-020 3.rs +27 -0
  644. package/dist/packages/crucible/seeds/rust/seed-020.rs +27 -0
  645. package/dist/packages/crucible/seeds/rust/seed-021 2.rs +26 -0
  646. package/dist/packages/crucible/seeds/rust/seed-021 3.rs +26 -0
  647. package/dist/packages/crucible/seeds/rust/seed-021.rs +26 -0
  648. package/dist/packages/crucible/seeds/rust/seed-022 2.rs +23 -0
  649. package/dist/packages/crucible/seeds/rust/seed-022 3.rs +23 -0
  650. package/dist/packages/crucible/seeds/rust/seed-022.rs +23 -0
  651. package/dist/packages/crucible/seeds/rust/seed-023 2.rs +22 -0
  652. package/dist/packages/crucible/seeds/rust/seed-023 3.rs +22 -0
  653. package/dist/packages/crucible/seeds/rust/seed-023.rs +22 -0
  654. package/dist/packages/crucible/seeds/rust/seed-024 2.rs +24 -0
  655. package/dist/packages/crucible/seeds/rust/seed-024 3.rs +24 -0
  656. package/dist/packages/crucible/seeds/rust/seed-024.rs +24 -0
  657. package/dist/packages/crucible/seeds/rust/seed-025 2.rs +29 -0
  658. package/dist/packages/crucible/seeds/rust/seed-025 3.rs +29 -0
  659. package/dist/packages/crucible/seeds/rust/seed-025.rs +29 -0
  660. package/dist/packages/crucible/seeds/rust/seed-026 2.rs +23 -0
  661. package/dist/packages/crucible/seeds/rust/seed-026 3.rs +23 -0
  662. package/dist/packages/crucible/seeds/rust/seed-026.rs +23 -0
  663. package/dist/packages/crucible/seeds/rust/seed-027 2.rs +24 -0
  664. package/dist/packages/crucible/seeds/rust/seed-027 3.rs +24 -0
  665. package/dist/packages/crucible/seeds/rust/seed-027.rs +24 -0
  666. package/dist/packages/crucible/seeds/rust/seed-028 2.rs +25 -0
  667. package/dist/packages/crucible/seeds/rust/seed-028 3.rs +25 -0
  668. package/dist/packages/crucible/seeds/rust/seed-028.rs +25 -0
  669. package/dist/packages/crucible/seeds/rust/seed-029 2.rs +25 -0
  670. package/dist/packages/crucible/seeds/rust/seed-029 3.rs +25 -0
  671. package/dist/packages/crucible/seeds/rust/seed-029.rs +25 -0
  672. package/dist/packages/crucible/seeds/rust/seed-030 2.rs +30 -0
  673. package/dist/packages/crucible/seeds/rust/seed-030 3.rs +30 -0
  674. package/dist/packages/crucible/seeds/rust/seed-030.rs +30 -0
  675. package/dist/packages/crucible/seeds/rust/seed-031 2.rs +22 -0
  676. package/dist/packages/crucible/seeds/rust/seed-031 3.rs +22 -0
  677. package/dist/packages/crucible/seeds/rust/seed-031.rs +22 -0
  678. package/dist/packages/crucible/seeds/rust/seed-032 2.rs +25 -0
  679. package/dist/packages/crucible/seeds/rust/seed-032 3.rs +25 -0
  680. package/dist/packages/crucible/seeds/rust/seed-032.rs +25 -0
  681. package/dist/packages/crucible/seeds/rust/seed-033 2.rs +25 -0
  682. package/dist/packages/crucible/seeds/rust/seed-033 3.rs +25 -0
  683. package/dist/packages/crucible/seeds/rust/seed-033.rs +25 -0
  684. package/dist/packages/crucible/seeds/rust/seed-034 2.rs +20 -0
  685. package/dist/packages/crucible/seeds/rust/seed-034 3.rs +20 -0
  686. package/dist/packages/crucible/seeds/rust/seed-034.rs +20 -0
  687. package/dist/packages/crucible/seeds/rust/seed-035 2.rs +28 -0
  688. package/dist/packages/crucible/seeds/rust/seed-035 3.rs +28 -0
  689. package/dist/packages/crucible/seeds/rust/seed-035.rs +28 -0
  690. package/dist/packages/crucible/seeds/rust/seed-036 2.rs +26 -0
  691. package/dist/packages/crucible/seeds/rust/seed-036 3.rs +26 -0
  692. package/dist/packages/crucible/seeds/rust/seed-036.rs +26 -0
  693. package/dist/packages/crucible/seeds/rust/seed-037 2.rs +31 -0
  694. package/dist/packages/crucible/seeds/rust/seed-037 3.rs +31 -0
  695. package/dist/packages/crucible/seeds/rust/seed-037.rs +31 -0
  696. package/dist/packages/crucible/seeds/rust/seed-038 2.rs +25 -0
  697. package/dist/packages/crucible/seeds/rust/seed-038 3.rs +25 -0
  698. package/dist/packages/crucible/seeds/rust/seed-038.rs +25 -0
  699. package/dist/packages/crucible/seeds/rust/seed-039 2.rs +28 -0
  700. package/dist/packages/crucible/seeds/rust/seed-039 3.rs +28 -0
  701. package/dist/packages/crucible/seeds/rust/seed-039.rs +28 -0
  702. package/dist/packages/crucible/seeds/rust/seed-040 2.rs +27 -0
  703. package/dist/packages/crucible/seeds/rust/seed-040 3.rs +27 -0
  704. package/dist/packages/crucible/seeds/rust/seed-040.rs +27 -0
  705. package/dist/packages/crucible/seeds/rust/seed-041 2.rs +32 -0
  706. package/dist/packages/crucible/seeds/rust/seed-041 3.rs +32 -0
  707. package/dist/packages/crucible/seeds/rust/seed-041.rs +32 -0
  708. package/dist/packages/crucible/seeds/rust/seed-042 2.rs +27 -0
  709. package/dist/packages/crucible/seeds/rust/seed-042 3.rs +27 -0
  710. package/dist/packages/crucible/seeds/rust/seed-042.rs +27 -0
  711. package/dist/packages/crucible/seeds/rust/seed-043 2.rs +29 -0
  712. package/dist/packages/crucible/seeds/rust/seed-043 3.rs +29 -0
  713. package/dist/packages/crucible/seeds/rust/seed-043.rs +29 -0
  714. package/dist/packages/crucible/seeds/rust/seed-044 2.rs +25 -0
  715. package/dist/packages/crucible/seeds/rust/seed-044 3.rs +25 -0
  716. package/dist/packages/crucible/seeds/rust/seed-044.rs +25 -0
  717. package/dist/packages/crucible/seeds/rust/seed-045 2.rs +28 -0
  718. package/dist/packages/crucible/seeds/rust/seed-045 3.rs +28 -0
  719. package/dist/packages/crucible/seeds/rust/seed-045.rs +28 -0
  720. package/dist/packages/crucible/seeds/rust/seed-046 2.rs +25 -0
  721. package/dist/packages/crucible/seeds/rust/seed-046 3.rs +25 -0
  722. package/dist/packages/crucible/seeds/rust/seed-046.rs +25 -0
  723. package/dist/packages/crucible/seeds/rust/seed-047 2.rs +34 -0
  724. package/dist/packages/crucible/seeds/rust/seed-047 3.rs +34 -0
  725. package/dist/packages/crucible/seeds/rust/seed-047.rs +34 -0
  726. package/dist/packages/crucible/seeds/rust/seed-048 2.rs +21 -0
  727. package/dist/packages/crucible/seeds/rust/seed-048 3.rs +21 -0
  728. package/dist/packages/crucible/seeds/rust/seed-048.rs +21 -0
  729. package/dist/packages/crucible/seeds/rust/seed-049 2.rs +26 -0
  730. package/dist/packages/crucible/seeds/rust/seed-049 3.rs +26 -0
  731. package/dist/packages/crucible/seeds/rust/seed-049.rs +26 -0
  732. package/dist/packages/crucible/seeds/rust/seed-050 2.rs +23 -0
  733. package/dist/packages/crucible/seeds/rust/seed-050 3.rs +23 -0
  734. package/dist/packages/crucible/seeds/rust/seed-050.rs +23 -0
  735. package/dist/packages/crucible/seeds/ts/seed-001 2.ts +32 -0
  736. package/dist/packages/crucible/seeds/ts/seed-001 3.ts +32 -0
  737. package/dist/packages/crucible/seeds/ts/seed-001.ts +32 -0
  738. package/dist/packages/crucible/seeds/ts/seed-002 2.ts +34 -0
  739. package/dist/packages/crucible/seeds/ts/seed-002 3.ts +34 -0
  740. package/dist/packages/crucible/seeds/ts/seed-002.ts +34 -0
  741. package/dist/packages/crucible/seeds/ts/seed-003 2.ts +28 -0
  742. package/dist/packages/crucible/seeds/ts/seed-003 3.ts +28 -0
  743. package/dist/packages/crucible/seeds/ts/seed-003.ts +28 -0
  744. package/dist/packages/crucible/seeds/ts/seed-004 2.ts +34 -0
  745. package/dist/packages/crucible/seeds/ts/seed-004 3.ts +34 -0
  746. package/dist/packages/crucible/seeds/ts/seed-004.ts +34 -0
  747. package/dist/packages/crucible/seeds/ts/seed-005 2.ts +32 -0
  748. package/dist/packages/crucible/seeds/ts/seed-005 3.ts +32 -0
  749. package/dist/packages/crucible/seeds/ts/seed-005.ts +32 -0
  750. package/dist/packages/crucible/seeds/ts/seed-006 2.ts +31 -0
  751. package/dist/packages/crucible/seeds/ts/seed-006 3.ts +31 -0
  752. package/dist/packages/crucible/seeds/ts/seed-006.ts +31 -0
  753. package/dist/packages/crucible/seeds/ts/seed-007 2.ts +28 -0
  754. package/dist/packages/crucible/seeds/ts/seed-007 3.ts +28 -0
  755. package/dist/packages/crucible/seeds/ts/seed-007.ts +28 -0
  756. package/dist/packages/crucible/seeds/ts/seed-008 2.ts +40 -0
  757. package/dist/packages/crucible/seeds/ts/seed-008 3.ts +40 -0
  758. package/dist/packages/crucible/seeds/ts/seed-008.ts +40 -0
  759. package/dist/packages/crucible/seeds/ts/seed-009 2.ts +31 -0
  760. package/dist/packages/crucible/seeds/ts/seed-009 3.ts +31 -0
  761. package/dist/packages/crucible/seeds/ts/seed-009.ts +31 -0
  762. package/dist/packages/crucible/seeds/ts/seed-010 2.ts +33 -0
  763. package/dist/packages/crucible/seeds/ts/seed-010 3.ts +33 -0
  764. package/dist/packages/crucible/seeds/ts/seed-010.ts +33 -0
  765. package/dist/packages/crucible/seeds/ts/seed-011 2.ts +29 -0
  766. package/dist/packages/crucible/seeds/ts/seed-011 3.ts +29 -0
  767. package/dist/packages/crucible/seeds/ts/seed-011.ts +29 -0
  768. package/dist/packages/crucible/seeds/ts/seed-012 2.ts +34 -0
  769. package/dist/packages/crucible/seeds/ts/seed-012 3.ts +34 -0
  770. package/dist/packages/crucible/seeds/ts/seed-012.ts +34 -0
  771. package/dist/packages/crucible/seeds/ts/seed-013 2.ts +31 -0
  772. package/dist/packages/crucible/seeds/ts/seed-013 3.ts +31 -0
  773. package/dist/packages/crucible/seeds/ts/seed-013.ts +31 -0
  774. package/dist/packages/crucible/seeds/ts/seed-014 2.ts +36 -0
  775. package/dist/packages/crucible/seeds/ts/seed-014 3.ts +36 -0
  776. package/dist/packages/crucible/seeds/ts/seed-014.ts +36 -0
  777. package/dist/packages/crucible/seeds/ts/seed-015 2.ts +31 -0
  778. package/dist/packages/crucible/seeds/ts/seed-015 3.ts +31 -0
  779. package/dist/packages/crucible/seeds/ts/seed-015.ts +31 -0
  780. package/dist/packages/crucible/seeds/ts/seed-016 2.ts +37 -0
  781. package/dist/packages/crucible/seeds/ts/seed-016 3.ts +37 -0
  782. package/dist/packages/crucible/seeds/ts/seed-016.ts +37 -0
  783. package/dist/packages/crucible/seeds/ts/seed-017 2.ts +44 -0
  784. package/dist/packages/crucible/seeds/ts/seed-017 3.ts +44 -0
  785. package/dist/packages/crucible/seeds/ts/seed-017.ts +44 -0
  786. package/dist/packages/crucible/seeds/ts/seed-018 2.ts +33 -0
  787. package/dist/packages/crucible/seeds/ts/seed-018 3.ts +33 -0
  788. package/dist/packages/crucible/seeds/ts/seed-018.ts +33 -0
  789. package/dist/packages/crucible/seeds/ts/seed-019 2.ts +32 -0
  790. package/dist/packages/crucible/seeds/ts/seed-019 3.ts +32 -0
  791. package/dist/packages/crucible/seeds/ts/seed-019.ts +32 -0
  792. package/dist/packages/crucible/seeds/ts/seed-020 2.ts +33 -0
  793. package/dist/packages/crucible/seeds/ts/seed-020 3.ts +33 -0
  794. package/dist/packages/crucible/seeds/ts/seed-020.ts +33 -0
  795. package/dist/packages/crucible/seeds/ts/seed-021 2.ts +33 -0
  796. package/dist/packages/crucible/seeds/ts/seed-021 3.ts +33 -0
  797. package/dist/packages/crucible/seeds/ts/seed-021.ts +33 -0
  798. package/dist/packages/crucible/seeds/ts/seed-022 2.ts +34 -0
  799. package/dist/packages/crucible/seeds/ts/seed-022 3.ts +34 -0
  800. package/dist/packages/crucible/seeds/ts/seed-022.ts +34 -0
  801. package/dist/packages/crucible/seeds/ts/seed-023 2.ts +33 -0
  802. package/dist/packages/crucible/seeds/ts/seed-023 3.ts +33 -0
  803. package/dist/packages/crucible/seeds/ts/seed-023.ts +33 -0
  804. package/dist/packages/crucible/seeds/ts/seed-024 2.ts +35 -0
  805. package/dist/packages/crucible/seeds/ts/seed-024 3.ts +35 -0
  806. package/dist/packages/crucible/seeds/ts/seed-024.ts +35 -0
  807. package/dist/packages/crucible/seeds/ts/seed-025 2.ts +29 -0
  808. package/dist/packages/crucible/seeds/ts/seed-025 3.ts +29 -0
  809. package/dist/packages/crucible/seeds/ts/seed-025.ts +29 -0
  810. package/dist/packages/crucible/seeds/ts/seed-026 2.ts +36 -0
  811. package/dist/packages/crucible/seeds/ts/seed-026 3.ts +36 -0
  812. package/dist/packages/crucible/seeds/ts/seed-026.ts +36 -0
  813. package/dist/packages/crucible/seeds/ts/seed-027 2.ts +30 -0
  814. package/dist/packages/crucible/seeds/ts/seed-027 3.ts +30 -0
  815. package/dist/packages/crucible/seeds/ts/seed-027.ts +30 -0
  816. package/dist/packages/crucible/seeds/ts/seed-028 2.ts +32 -0
  817. package/dist/packages/crucible/seeds/ts/seed-028 3.ts +32 -0
  818. package/dist/packages/crucible/seeds/ts/seed-028.ts +32 -0
  819. package/dist/packages/crucible/seeds/ts/seed-029 2.ts +34 -0
  820. package/dist/packages/crucible/seeds/ts/seed-029 3.ts +34 -0
  821. package/dist/packages/crucible/seeds/ts/seed-029.ts +34 -0
  822. package/dist/packages/crucible/seeds/ts/seed-030 2.ts +37 -0
  823. package/dist/packages/crucible/seeds/ts/seed-030 3.ts +37 -0
  824. package/dist/packages/crucible/seeds/ts/seed-030.ts +37 -0
  825. package/dist/packages/crucible/seeds/ts/seed-031 2.ts +31 -0
  826. package/dist/packages/crucible/seeds/ts/seed-031 3.ts +31 -0
  827. package/dist/packages/crucible/seeds/ts/seed-031.ts +31 -0
  828. package/dist/packages/crucible/seeds/ts/seed-032 2.ts +32 -0
  829. package/dist/packages/crucible/seeds/ts/seed-032 3.ts +32 -0
  830. package/dist/packages/crucible/seeds/ts/seed-032.ts +32 -0
  831. package/dist/packages/crucible/seeds/ts/seed-033 2.ts +32 -0
  832. package/dist/packages/crucible/seeds/ts/seed-033 3.ts +32 -0
  833. package/dist/packages/crucible/seeds/ts/seed-033.ts +32 -0
  834. package/dist/packages/crucible/seeds/ts/seed-034 2.ts +34 -0
  835. package/dist/packages/crucible/seeds/ts/seed-034 3.ts +34 -0
  836. package/dist/packages/crucible/seeds/ts/seed-034.ts +34 -0
  837. package/dist/packages/crucible/seeds/ts/seed-035 2.ts +29 -0
  838. package/dist/packages/crucible/seeds/ts/seed-035 3.ts +29 -0
  839. package/dist/packages/crucible/seeds/ts/seed-035.ts +29 -0
  840. package/dist/packages/crucible/seeds/ts/seed-036 2.ts +34 -0
  841. package/dist/packages/crucible/seeds/ts/seed-036 3.ts +34 -0
  842. package/dist/packages/crucible/seeds/ts/seed-036.ts +34 -0
  843. package/dist/packages/crucible/seeds/ts/seed-037 2.ts +33 -0
  844. package/dist/packages/crucible/seeds/ts/seed-037 3.ts +33 -0
  845. package/dist/packages/crucible/seeds/ts/seed-037.ts +33 -0
  846. package/dist/packages/crucible/seeds/ts/seed-038 2.ts +29 -0
  847. package/dist/packages/crucible/seeds/ts/seed-038 3.ts +29 -0
  848. package/dist/packages/crucible/seeds/ts/seed-038.ts +29 -0
  849. package/dist/packages/crucible/seeds/ts/seed-039 2.ts +35 -0
  850. package/dist/packages/crucible/seeds/ts/seed-039 3.ts +35 -0
  851. package/dist/packages/crucible/seeds/ts/seed-039.ts +35 -0
  852. package/dist/packages/crucible/seeds/ts/seed-040 2.ts +31 -0
  853. package/dist/packages/crucible/seeds/ts/seed-040 3.ts +31 -0
  854. package/dist/packages/crucible/seeds/ts/seed-040.ts +31 -0
  855. package/dist/packages/crucible/seeds/ts/seed-041 2.ts +29 -0
  856. package/dist/packages/crucible/seeds/ts/seed-041 3.ts +29 -0
  857. package/dist/packages/crucible/seeds/ts/seed-041.ts +29 -0
  858. package/dist/packages/crucible/seeds/ts/seed-042 2.ts +33 -0
  859. package/dist/packages/crucible/seeds/ts/seed-042 3.ts +33 -0
  860. package/dist/packages/crucible/seeds/ts/seed-042.ts +33 -0
  861. package/dist/packages/crucible/seeds/ts/seed-043 2.ts +32 -0
  862. package/dist/packages/crucible/seeds/ts/seed-043 3.ts +32 -0
  863. package/dist/packages/crucible/seeds/ts/seed-043.ts +32 -0
  864. package/dist/packages/crucible/seeds/ts/seed-044 2.ts +36 -0
  865. package/dist/packages/crucible/seeds/ts/seed-044 3.ts +36 -0
  866. package/dist/packages/crucible/seeds/ts/seed-044.ts +36 -0
  867. package/dist/packages/crucible/seeds/ts/seed-045 2.ts +39 -0
  868. package/dist/packages/crucible/seeds/ts/seed-045 3.ts +39 -0
  869. package/dist/packages/crucible/seeds/ts/seed-045.ts +39 -0
  870. package/dist/packages/crucible/seeds/ts/seed-046 2.ts +41 -0
  871. package/dist/packages/crucible/seeds/ts/seed-046 3.ts +41 -0
  872. package/dist/packages/crucible/seeds/ts/seed-046.ts +41 -0
  873. package/dist/packages/crucible/seeds/ts/seed-047 2.ts +33 -0
  874. package/dist/packages/crucible/seeds/ts/seed-047 3.ts +33 -0
  875. package/dist/packages/crucible/seeds/ts/seed-047.ts +33 -0
  876. package/dist/packages/crucible/seeds/ts/seed-048 2.ts +33 -0
  877. package/dist/packages/crucible/seeds/ts/seed-048 3.ts +33 -0
  878. package/dist/packages/crucible/seeds/ts/seed-048.ts +33 -0
  879. package/dist/packages/crucible/seeds/ts/seed-049 2.ts +34 -0
  880. package/dist/packages/crucible/seeds/ts/seed-049 3.ts +34 -0
  881. package/dist/packages/crucible/seeds/ts/seed-049.ts +34 -0
  882. package/dist/packages/crucible/seeds/ts/seed-050 2.ts +41 -0
  883. package/dist/packages/crucible/seeds/ts/seed-050 3.ts +41 -0
  884. package/dist/packages/crucible/seeds/ts/seed-050.ts +41 -0
  885. package/dist/packages/crucible/seeds/ts/seed-051 2.ts +34 -0
  886. package/dist/packages/crucible/seeds/ts/seed-051 3.ts +34 -0
  887. package/dist/packages/crucible/seeds/ts/seed-051.ts +34 -0
  888. package/dist/packages/crucible/seeds/ts/seed-052 2.ts +36 -0
  889. package/dist/packages/crucible/seeds/ts/seed-052 3.ts +36 -0
  890. package/dist/packages/crucible/seeds/ts/seed-052.ts +36 -0
  891. package/dist/packages/crucible/self-attack.js +1 -0
  892. package/dist/packages/scanner/ai-confidence-scorer.js +1 -0
  893. package/dist/packages/scanner/alert-manager.js +1 -0
  894. package/dist/packages/scanner/ast-analyzer.js +1 -0
  895. package/dist/packages/scanner/baseline-manager.js +1 -0
  896. package/dist/packages/scanner/cfg-analyzer.js +1 -0
  897. package/dist/packages/scanner/code-scanner.js +1 -0
  898. package/dist/packages/scanner/codebase-passport.js +1 -0
  899. package/dist/packages/scanner/copy-paste-detector.js +1 -0
  900. package/dist/packages/scanner/dependency-graph.js +1 -0
  901. package/dist/packages/scanner/drift-detector.js +1 -0
  902. package/dist/packages/scanner/executive-report.js +1 -0
  903. package/dist/packages/scanner/export-verifier.js +1 -0
  904. package/dist/packages/scanner/file-collector.js +1 -0
  905. package/dist/packages/scanner/file-watcher.js +1 -0
  906. package/dist/packages/scanner/ghost-code-detector.js +1 -0
  907. package/dist/packages/scanner/go-ast-analyzer.js +1 -0
  908. package/dist/packages/scanner/hallucination-detector.js +1 -0
  909. package/dist/packages/scanner/health-checker.js +1 -0
  910. package/dist/packages/scanner/html-report.js +1 -0
  911. package/dist/packages/scanner/index.js +1 -0
  912. package/dist/packages/scanner/license-manager.js +1 -0
  913. package/dist/packages/scanner/master-health-checker.js +1 -0
  914. package/dist/packages/scanner/monitor-notifier.js +1 -0
  915. package/dist/packages/scanner/monitor-service.js +1 -0
  916. package/dist/packages/scanner/monitor-store.js +1 -0
  917. package/dist/packages/scanner/pre-commit-gate.js +1 -0
  918. package/dist/packages/scanner/python-ast-analyzer.js +1 -0
  919. package/dist/packages/scanner/scan-diff.js +1 -0
  920. package/dist/packages/scanner/scan-runner.js +1 -0
  921. package/dist/packages/scanner/secret-scanner.js +1 -0
  922. package/dist/packages/scanner/sentinel-core.js +1 -0
  923. package/dist/packages/scanner/sentinel-knowledge.js +1 -0
  924. package/dist/packages/scanner/taint-tracker.js +1 -0
  925. package/dist/packages/scanner/tech-debt-analyzer.js +1 -0
  926. package/dist/packages/scanner/tech-debt-cost.js +1 -0
  927. package/dist/packages/scanner/widget-generator.js +1 -0
  928. package/package.json +15 -7
  929. package/cli.js +0 -2627
  930. package/packages/scanner/ai-confidence-scorer.js +0 -260
  931. package/packages/scanner/alert-manager.js +0 -398
  932. package/packages/scanner/baseline-manager.js +0 -109
  933. package/packages/scanner/code-scanner.js +0 -758
  934. package/packages/scanner/codebase-passport.js +0 -299
  935. package/packages/scanner/copy-paste-detector.js +0 -276
  936. package/packages/scanner/dependency-graph.js +0 -541
  937. package/packages/scanner/drift-detector.js +0 -423
  938. package/packages/scanner/executive-report.js +0 -774
  939. package/packages/scanner/file-collector.js +0 -64
  940. package/packages/scanner/file-watcher.js +0 -323
  941. package/packages/scanner/ghost-code-detector.js +0 -301
  942. package/packages/scanner/hallucination-detector.js +0 -822
  943. package/packages/scanner/health-checker.js +0 -586
  944. package/packages/scanner/html-report.js +0 -634
  945. package/packages/scanner/index.js +0 -100
  946. package/packages/scanner/license-manager.js +0 -331
  947. package/packages/scanner/master-health-checker.js +0 -513
  948. package/packages/scanner/monitor-notifier.js +0 -64
  949. package/packages/scanner/monitor-service.js +0 -103
  950. package/packages/scanner/monitor-store.js +0 -117
  951. package/packages/scanner/pre-commit-gate.js +0 -216
  952. package/packages/scanner/scan-diff.js +0 -50
  953. package/packages/scanner/scan-runner.js +0 -172
  954. package/packages/scanner/secret-scanner.js +0 -612
  955. package/packages/scanner/secret-scanner.test.js +0 -103
  956. package/packages/scanner/sentinel-core.js +0 -616
  957. package/packages/scanner/sentinel-knowledge.js +0 -322
  958. package/packages/scanner/tech-debt-analyzer.js +0 -583
  959. package/packages/scanner/tech-debt-cost.js +0 -194
  960. package/packages/scanner/widget-generator.js +0 -415
@@ -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};
@@ -0,0 +1 @@
1
+ const fs=require("fs"),path=require("path"),HallucinationDetector=require("./hallucination-detector.js"),RISK_SIGNALS={evalUsage:/\beval\s*\(/,hardcodedSecret:/['"](?:sk-|pk-|xox[pboa]-|AKIA|ghp_|gho_|github_pat_)[a-zA-Z0-9_-]{10,}['"]|['"][a-zA-Z0-9]{32,}['"]|password\s*[:=]\s*['"][^'"]+['"]|["']password["']\s*:\s*["'][^"']+["']/i,commandInjection:/\b(?:child_process\.)?(?:exec|execSync|spawn|spawnSync|execFile|execFileSync)\s*\(\s*(?:`[^`]*\$\{|[^,)]*\+\s*\w|[^,)]*\+\s*['"`])/,sqlInjection:/\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM|WHERE)\b.*(\$\{|\+\s*['"]|\+\s*\w+|f['"].*\{|%s|%d)/i},AI_SIGNALS={genericNames:["data","result","response","output","input","temp","item","element","val","obj","arr","str","num","flag","status"],aiCommentPatterns:[/\/\/\s*TODO:?\s*implement/i,/\/\/\s*handle\s+error/i,/\/\/\s*add\s+error\s+handling/i,/\/\/\s*you\s+(can|may|might|should)/i,/\/\/\s*replace\s+(this|with)/i,/\/\/\s*this\s+(function|method|class)\s+(takes|accepts|returns|handles)/i,/\/\/\s*example\s+usage/i,/\/\/\s*note:?\s*this/i,/\/\*\*[\s\S]*?@description\s/,/\/\/\s*import.*if\s+needed/i],aiStructurePatterns:[/try\s*\{[\s\S]*?\}\s*catch\s*\(\s*(?:error|err|e)\s*\)\s*\{[\s\S]*?console\.(log|error)\s*\(\s*(?:error|err|e)\s*\)/,/if\s*\(!.*\)\s*\{?\s*(?:return|throw)\s/]};class AIConfidenceScorer{constructor(e={}){this.rootPath=e.rootPath||process.cwd(),this.hallDetector=e.hallucinationDetector||new HallucinationDetector({rootPath:this.rootPath})}async scan(e){const t=[];let s=new Map;try{const t=await this.hallDetector.scan(e),n=[...t.phantomAPIs||[],...t.deprecatedAPIs||[]];for(const e of n)s.has(e.file)||s.set(e.file,[]),s.get(e.file).push(e)}catch(e){}for(const n of e){let e;try{if(fs.statSync(n).size>524288)continue;e=fs.readFileSync(n,"utf-8")}catch(e){continue}const i=path.relative(this.rootPath,n);if(this._isSkippable(i))continue;const r=s.get(i)||[],c=this._extractFunctions(e,i,r);t.push(...c)}t.sort((e,t)=>t.confidence-e.confidence);const n=t.filter(e=>e.confidence>=70),i=t.filter(e=>e.confidence>=40&&e.confidence<70),r=t.filter(e=>e.riskScore>=70),c=t.filter(e=>e.riskScore>=40&&e.riskScore<70);return{functions:t,highConfidence:n,mediumConfidence:i,totalFunctions:t.length,aiLikelyCount:n.length,aiPossibleCount:i.length,aiPercentage:t.length>0?Math.round(n.length/t.length*100):0,highRisk:r,mediumRisk:c,highRiskCount:r.length,mediumRiskCount:c.length}}_extractFunctions(e,t,s=[]){const n=e.split("\n"),i=[];for(let e=0;e<n.length;e++){const r=this._detectFunction(n,e);if(!r)continue;const c=this._findFunctionEnd(n,e),o=n.slice(e,c+1).join("\n"),a=n.slice(e,c+1),l=e+1,h=c+1,u=this._scoreFunction(r.name,o,a,n,e),m=this._scoreRisk(o,a,s,l,h);i.push({file:t,name:r.name,line:e+1,lineCount:c-e+1,confidence:u.score,signals:u.reasons,riskScore:m.score,riskSignals:m.reasons,riskSeverity:m.score>=70?"critical":m.score>=40?"high":m.score>=15?"medium":"low",testCoverage:this._hasTests(r.name,t)?"found":"none",category:"ai_confidence",severity:u.score>=80?"high":u.score>=50?"medium":"low"}),e=c}return i}_scoreRisk(e,t,s,n,i){let r=0;const c=[];RISK_SIGNALS.evalUsage.test(e)&&(r+=40,c.push("eval() usage — arbitrary code execution risk"));const o=s.filter(e=>e.line>=n&&e.line<=i);o.length>0&&(r+=Math.min(30,15*o.length),c.push(`${o.length} hallucinated/deprecated API usage(s): ${o.map(e=>e.name).slice(0,3).join(", ")}`));const a=this._maxBraceDepth(t);a>3&&(r+=Math.min(20,8*(a-3)),c.push(`Deep nesting — ${a} levels (>3 is risky)`));const l=this._cyclomaticComplexity(e);return l>10&&(r+=Math.min(15,Math.round(1.5*(l-10))),c.push(`High cyclomatic complexity (${l})`)),RISK_SIGNALS.hardcodedSecret.test(e)&&(r+=25,c.push("Hardcoded secret/credential detected")),RISK_SIGNALS.commandInjection.test(e)&&(r+=25,c.push("Command injection risk — exec/spawn with unsanitized string concatenation")),RISK_SIGNALS.sqlInjection.test(e)&&(r+=25,c.push("SQL injection risk — query built via string concatenation/interpolation")),r=Math.min(100,r),{score:r,reasons:c}}_maxBraceDepth(e){let t=0,s=0;for(const n of e)for(const e of n)"{"===e?(t++,t>s&&(s=t)):"}"===e&&(t=Math.max(0,t-1));return Math.max(0,s-1)}_cyclomaticComplexity(e){return 1+(e.match(/\b(if|else if|for|while|case|catch)\b|\?\s*[^:]+:|&&|\|\|/g)||[]).length}_scoreFunction(e,t,s,n,i){let r=0;const c=[],o=AI_SIGNALS.genericNames.filter(e=>{const s=new RegExp(`\\b${e}\\b`,"g");return(t.match(s)||[]).length>=2}).length;o>=3?(r+=15,c.push(`${o} generic variable names (data, result, response...)`)):o>=1&&(r+=8,c.push(`${o} generic variable name(s)`));const a=AI_SIGNALS.aiCommentPatterns.filter(e=>e.test(t)).length;if(a>=2?(r+=20,c.push(`${a} AI-style comments (TODO: implement, handle error, etc.)`)):a>=1&&(r+=10,c.push("AI-style comment detected")),i>0){const e=n.slice(Math.max(0,i-10),i).join("\n");/\/\*\*[\s\S]*@param[\s\S]*@returns?[\s\S]*@(?:throws|example)/s.test(e)&&(r+=10,c.push("Exhaustive JSDoc (params + returns + throws/example)"))}(t.match(/try\s*\{/g)||[]).length>=2&&(r+=10,c.push("Multiple try/catch blocks (AI over-handles errors)"));const l=s.length;return l>=50?(r+=15,c.push(`${l} lines — large self-contained function`)):l>=25&&(r+=8,c.push(`${l} lines`)),0===n.slice(0,i).concat(n.slice(i+l)).filter(t=>t.includes(e)).length&&l>10&&(r+=10,c.push("Isolated — no internal references")),/catch\s*\([^)]*\)\s*\{[^}]*console\.(log|error)\s*\(/.test(t)&&(r+=8,c.push("console.log/error in catch block (AI default pattern)")),/=>\s*\{[\s\n\r]*const\s*\{/.test(t)&&(r+=5,c.push("Immediate destructuring after arrow (common AI pattern)")),(t.match(/`[^`]*\$\{/g)||[]).length>=4&&(r+=5,c.push("Heavy template literal usage")),r=Math.min(100,r),{score:r,reasons:c}}_detectFunction(e,t){const s=e[t].trim();let n=s.match(/^(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);return n?{name:n[1]}:(n=s.match(/^(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s+)?(?:function|\()/),n&&(s.includes("=>")||s.includes("function"))?{name:n[1]}:null)}_findFunctionEnd(e,t){let s=0,n=!1;for(let i=t;i<e.length&&i<t+300;i++){for(const t of e[i])"{"===t&&(s++,n=!0),"}"===t&&s--;if(n&&s<=0)return i}return Math.min(t+20,e.length-1)}_hasTests(e,t){return[t.replace(/\.(js|ts)$/,".test.$1"),t.replace(/\.(js|ts)$/,".spec.$1"),t.replace(/src\//,"test/"),t.replace(/src\//,"__tests__/")].some(e=>{try{return fs.existsSync(path.resolve(this.rootPath,e))}catch(e){return!1}})}_isSkippable(e){return[/\.test\./i,/\.spec\./i,/test[\/\\]/i,/spec[\/\\]/i,/__test__/i,/__mocks__/i,/\.config\./i,/\.d\.ts$/,/node_modules/i,/\.min\./i].some(t=>t.test(e))}}module.exports=AIConfidenceScorer;
@@ -0,0 +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")}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}};
@@ -0,0 +1 @@
1
+ const fs=require("fs"),path=require("path"),crypto=require("crypto"),BASELINE_FILE=".thuban-baseline.json";class BaselineManager{constructor(e){this.rootPath=path.resolve(e),this.baselinePath=path.join(this.rootPath,BASELINE_FILE);const n=path.resolve(this.baselinePath);if(!n.startsWith(this.rootPath+path.sep)&&n!==this.rootPath)throw new Error(`[BaselineManager] Baseline path escapes root directory: ${n}`)}_atomicWriteSync(e,n){const s=e+".tmp."+process.pid+"."+Date.now();fs.writeFileSync(s,n,"utf-8"),fs.renameSync(s,e)}create(e){const n={version:1,created:(new Date).toISOString(),generator:"thuban",issueCount:e.length,issues:e.map(e=>({fingerprint:this._fingerprint(e),file:e.file,category:e.category||"unknown",severity:e.severity||"unknown",id:e.id||null,message:(e.message||"").substring(0,200)}))};return n._hmac=this._computeHmac(n),this._atomicWriteSync(this.baselinePath,JSON.stringify(n,null,2)),{path:this.baselinePath,count:e.length}}load(){try{const e=fs.readFileSync(this.baselinePath,"utf-8");if(e.length>10485760)return console.warn("[BASELINE] WARNING: Baseline file exceeds 10MB — ignoring"),null;const n=JSON.parse(e);if(null==n||"object"!=typeof n||Array.isArray(n))return console.warn("[BASELINE] WARNING: Baseline file has invalid root type — ignoring"),null;if("__proto__"in n||"constructor"in n||"prototype"in n)return console.warn("[BASELINE] WARNING: Baseline file contains prototype pollution keys — ignoring"),null;if(1!==n.version||"thuban"!==n.generator)return console.warn("[BASELINE] WARNING: Baseline file has invalid version/generator — ignoring"),null;if(!Array.isArray(n.issues))return console.warn("[BASELINE] WARNING: Baseline file has non-array issues field — ignoring"),null;if("number"!=typeof n.issueCount||n.issueCount!==n.issues.length)return console.warn("[BASELINE] WARNING: Baseline issueCount mismatch — ignoring"),null;for(const e of n.issues){if(null==e||"object"!=typeof e||Array.isArray(e))return console.warn("[BASELINE] WARNING: Baseline contains invalid issue entry — ignoring"),null;if("__proto__"in e||"constructor"in e)return console.warn("[BASELINE] WARNING: Baseline issue contains prototype pollution keys — ignoring"),null;if("string"!=typeof e.fingerprint||!/^[a-f0-9]{16}$/.test(e.fingerprint))return console.warn("[BASELINE] WARNING: Baseline issue has invalid fingerprint — ignoring"),null}if(n._hmac){const e=this._computeHmac(n);if(n._hmac!==e)return console.warn("[BASELINE] WARNING: Baseline HMAC mismatch — file may have been tampered with — ignoring"),null}else console.warn("[BASELINE] NOTE: Baseline file has no integrity tag — re-run `thuban baseline` to add one");return n}catch(e){return null}}filterNew(e){const n=this.load();if(!n||!n.issues)return{newIssues:e,baselinedIssues:[],baselineExists:!1};const s=new Set(n.issues.map(e=>e.fingerprint)),t=[],i=[];for(const n of e){const e=this._fingerprint(n);s.has(e)?i.push(n):t.push(n)}return{newIssues:t,baselinedIssues:i,baselineExists:!0,baselineDate:n.created,baselineCount:n.issueCount}}exists(){return fs.existsSync(this.baselinePath)}_fingerprint(e){const n=[e.file||"",e.category||"",e.id||"",(e.message||"").substring(0,100)].join("|");return crypto.createHash("sha256").update(n).digest("hex").substring(0,16)}_computeHmac(e){const n=JSON.stringify({version:e.version,created:e.created,generator:e.generator,issueCount:e.issueCount,issues:e.issues});return crypto.createHmac("sha256",this.baselinePath).update(n).digest("hex")}}module.exports=BaselineManager;
@@ -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}};
@@ -0,0 +1 @@
1
+ const fs=require("fs"),path=require("path"),EventEmitter=require("events"),astAnalyzer=require("./ast-analyzer"),taintTracker=require("./taint-tracker"),cfgAnalyzer=require("./cfg-analyzer"),{getCodeScannerPatterns:getCodeScannerPatterns}=require("../crucible/rule-loader.js");class CodeScanner extends EventEmitter{constructor(e={}){super();const t=new Set(["rootPath","ignorePatterns","maxFileSize","totalScanTimeout","perFileTimeout","complexityThreshold","nestingThreshold"]),s={};for(const n of Object.keys(e))t.has(n)&&(s[n]=e[n]);this.config={rootPath:s.rootPath||process.cwd(),ignorePatterns:s.ignorePatterns||["node_modules/**",".git/**","dist/**",".orion/snapshots/**"],maxFileSize:s.maxFileSize||5242880,totalScanTimeout:s.totalScanTimeout||3e5,perFileTimeout:s.perFileTimeout||1e4,complexityThreshold:s.complexityThreshold||15,nestingThreshold:s.nestingThreshold||4},this._scanStartTime=null,this.safePatterns=[/keyEnv\s*[:=]/i,/envVar\s*[:=]/i,/process\.env\[/,/process\.env\./,/model\s*[:=]\s*['"][^'"]+['"]/i,/version\s*[:=]\s*['"][^'"]+['"]/i,/claude-|gpt-|gemini-|llama-|mistral-|deepseek-|grok-|sonar-/i,/endpoint\s*[:=]/i,/https?:\/\//,/_API_KEY['"]/,/API_KEY['"]\s*\)/,/errors?\.\w+\s*=/i,/Error\s*[:=]/i,/required|invalid|must be|cannot be/i],this.testFilePatterns=[/\.test\.(js|ts|jsx|tsx)$/,/\.spec\.(js|ts|jsx|tsx)$/,/__tests__\//,/\.mock\.(js|ts)$/,/test-.*\.js$/],this.securityPatterns=getCodeScannerPatterns(),this.qualityPatterns=[{id:"QUAL001",name:"Console.log in Production",pattern:/console\.(log|debug|info)\(/,severity:"info",message:"console.log found - consider using proper logging",skipInTests:!0,skipInDirs:["test","scripts","tools","dev"]},{id:"QUAL002",name:"TODO Comment",pattern:/\/\/\s*TODO|\/\*\s*TODO|\*\s*TODO|#\s*TODO/i,severity:"info",message:"TODO comment found - track in issue system"},{id:"QUAL003",name:"FIXME Comment",pattern:/\/\/\s*FIXME|\/\*\s*FIXME|\*\s*FIXME|#\s*FIXME/i,severity:"warning",message:"FIXME comment found - needs attention"},{id:"QUAL004",name:"Empty Catch Block",pattern:/catch\s*\([^)]*\)\s*\{\s*\}/,severity:"warning",message:"Empty catch block swallows errors"},{id:"QUAL005",name:"Magic Number",pattern:/(?<![0-9a-zA-Z_])(?!(?:1000|2000|3000|3001|4000|5000|8000|8080|9000|80|443|24|60|1024|2048|4096)\b)\d{5,}(?![0-9])/,severity:"info",message:"Magic number detected - consider using named constant",skipInTests:!0},{id:"QUAL006",name:"Callback Hell",pattern:/\)\s*=>\s*\{[^}]*\)\s*=>\s*\{[^}]*\)\s*=>\s*\{/,severity:"warning",message:"Deeply nested callbacks - consider async/await"}],this.performancePatterns=[{id:"PERF001",name:"Sync FS in Async",pattern:/fs\.(readFileSync|writeFileSync|readdirSync|statSync)/,severity:"warning",message:"Synchronous file operation - may block event loop"},{id:"PERF002",name:"Missing Await",pattern:/async\s+function[^{]*\{[^}]*(?<!await\s)new\s+Promise/,severity:"warning",message:"Promise created in async function without await"},{id:"PERF003",name:"Loop Database Query",pattern:/for\s*\([^)]*\)\s*\{[^}]*(query|find|select|update)/i,severity:"error",message:"Possible N+1 query pattern - consider batch operation"},{id:"PERF004",name:"Large Array in Memory",pattern:/new\s+Array\s*\(\s*\d{6,}\s*\)/,severity:"warning",message:"Large array allocation - consider streaming"}],this.aiPatterns=[{id:"AI001",name:"Potentially Hallucinated Import",pattern:/require\(['"](?!\.)[^'"]+['"]\)/,severity:"info",message:"External dependency - verify it exists in package.json",check:(e,t,s)=>this._checkDependencyExists(e,s)},{id:"AI002",name:"Unused Import",pattern:/const\s+(\w+)\s*=\s*require\(['"][^'"]+['"]\)/,severity:"info",message:"Import may be unused - verify usage",check:(e,t)=>this._checkImportUsage(e,t)},{id:"AI003",name:"Over-Engineered Pattern",pattern:/class\s+\w+Factory\s*\{|AbstractFactory|Singleton\.getInstance/,severity:"info",message:"Complex pattern detected - ensure justified by requirements"}]}async scanAll(){console.log("[CODE-SCANNER] Starting full codebase scan...");const e=Date.now();this._scanStartTime=e;const t={filesScanned:0,filesSkipped:0,issues:[],summary:{},duration:0};try{const s=await this._discoverFiles(this.config.rootPath);console.log(`[CODE-SCANNER] Found ${s.length} files to scan`),this._exportTaintMap=this._buildExportTaintMap(s);for(const n of s){if(Date.now()-e>this.config.totalScanTimeout){console.warn(`[CODE-SCANNER] Total scan timeout (${Math.round(this.config.totalScanTimeout/1e3)}s) reached — stopping scan`),t.issues.push({file:"scan",category:"info",severity:"warning",id:"TIMEOUT001",message:`Scan stopped after ${Math.round(this.config.totalScanTimeout/1e3)}s timeout — ${s.length-t.filesScanned} files not scanned`});break}const i=Date.now(),r=await this.scanFile(n),a=Date.now()-i;a>this.config.perFileTimeout&&console.warn(`[CODE-SCANNER] File ${n} took ${a}ms (>${this.config.perFileTimeout}ms) — results included but flagged`),t.filesScanned++,t.issues.push(...r)}return this._exportTaintMap=null,t.duration=Date.now()-e,t.summary=this._summarizeIssues(t.issues),console.log(`[CODE-SCANNER] Scan complete: ${t.filesScanned} files, ${t.issues.length} issues`),t}catch(e){return console.error("[CODE-SCANNER] Scan failed"),t.error="Scan failed",t}}async scanFiles(e){const t=Date.now();this._scanStartTime=t;const s={filesScanned:0,filesSkipped:0,issues:[],summary:{}};this._exportTaintMap=this._buildExportTaintMap(e);for(const n of e){if(Date.now()-t>this.config.totalScanTimeout){console.warn(`[CODE-SCANNER] Total scan timeout (${Math.round(this.config.totalScanTimeout/1e3)}s) reached — stopping scan`),s.issues.push({file:"scan",category:"info",severity:"warning",id:"TIMEOUT001",message:`Scan stopped after ${Math.round(this.config.totalScanTimeout/1e3)}s timeout — ${e.length-s.filesScanned} files not scanned`});break}const i=Date.now(),r=await this.scanFile(n),a=Date.now()-i;a>this.config.perFileTimeout&&(console.warn(`[CODE-SCANNER] WARNING: ${n} took ${a}ms (>${this.config.perFileTimeout}ms) — skipping slow file`),s.filesSkipped++,s.issues.push({file:n,category:"performance",severity:"warning",id:"TIMEOUT002",message:`File scan took ${a}ms — exceeded ${this.config.perFileTimeout}ms per-file limit`})),s.filesScanned++,s.issues.push(...r)}return this._exportTaintMap=null,s.summary=this._summarizeIssues(s.issues),s}async scanFile(e){const t=[];try{const s=fs.statSync(e);if(s.isSymbolicLink&&s.isSymbolicLink())return[];if(0===s.size)return[];if(s.size>this.config.maxFileSize)return[{file:e,category:"quality",severity:"info",id:"FILE001",message:`File too large (${(s.size/1048576).toFixed(1)}MB > ${(this.config.maxFileSize/1048576).toFixed(0)}MB limit) - skipped`}];let n;try{n=fs.readFileSync(e)}catch(e){return[]}const i=Math.min(n.length,512);for(let e=0;e<i;e++)if(0===n[e])return[];const r=n.toString("utf8"),a=r.split("\n"),o=path.extname(e).toLowerCase();[".js",".ts",".jsx",".tsx",".mjs",".cjs",".py",".pyw",".go",".rs",".java",".kt",".cs",".php",".rb"].includes(o)&&(t.push(...this._runPatternScan(e,r,a,this.securityPatterns,"security")),t.push(...this._runPatternScan(e,r,a,this.qualityPatterns,"quality")),t.push(...this._runPatternScan(e,r,a,this.performancePatterns,"performance")),t.push(...this._runPatternScan(e,r,a,this.aiPatterns,"ai")),t.push(...this._analyzeComplexity(e,r,a)),t.push(...this._analyzeNesting(e,r,a)),t.push(...this._runAstScan(e,r,t)),t.push(...this._runTaintScan(e,r,t)),t.push(...this._runCfgScan(e,r,t))),".json"===o&&t.push(...this._scanJson(e,r));const c=this._applyThubanIgnore(a,t);for(const e of c)this.emit("issueFound",e);return c}catch(t){return[{file:e,category:"error",severity:"error",id:"SCAN001",message:"Failed to scan file: "+("ENOENT"===t.code?"File not found":"EACCES"===t.code?"Permission denied":"EISDIR"===t.code?"Path is a directory":"Scan error")}]}}_applyThubanIgnore(e,t){if(!t.length)return t;const s=new Map;for(let t=0;t<e.length;t++){const n=e[t].match(/thuban-ignore(?:\s+([A-Za-z0-9_,\s]+))?/);if(!n)continue;const i=n[1],r=[t+1,t+2];for(const e of r){if(!i||!i.trim()){s.set(e,!0);continue}const t=i.split(",").map(e=>e.trim()).filter(Boolean),n=s.get(e);if(!0===n)continue;const r=n instanceof Set?n:new Set;for(const e of t)r.add(e);s.set(e,r)}}return 0===s.size?t:t.filter(e=>{if(null==e.line)return!0;const t=s.get(e.line);return!t||!0!==t&&!t.has(e.id)})}getRules(){return{security:this.securityPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),quality:this.qualityPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),performance:this.performancePatterns.map(e=>({id:e.id,name:e.name,severity:e.severity})),ai:this.aiPatterns.map(e=>({id:e.id,name:e.name,severity:e.severity}))}}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const n=fs.readdirSync(e,{withFileTypes:!0});for(const i of n){const n=path.join(e,i.name),r=path.relative(this.config.rootPath,n);if(!this._shouldIgnore(r))if(i.isDirectory())await this._discoverFiles(n,t,s+1);else if(i.isFile()){const e=path.extname(i.name).toLowerCase();[".js",".ts",".jsx",".tsx",".json",".py",".pyw",".go",".rs",".java",".kt",".cs",".php",".rb"].includes(e)&&t.push(n)}}}catch(e){}return t}_shouldIgnore(e){const t=e.replace(/\\/g,"/"),s=["node_modules",".git","dist",".vite"];for(const e of s)if(t.includes("/"+e+"/")||t.startsWith(e+"/")||t===e||t.endsWith("/"+e))return!0;for(const e of this.config.ignorePatterns){const s=e.replace(/\\/g,"/"),n=s.split("/")[0].replace(/\*/g,"");if(n&&(t===n||t.startsWith(n+"/")))return!0;let i=s.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");if(new RegExp("^"+i).test(t))return!0}return!1}_runPatternScan(e,t,s,n,i){const r=[],a=path.basename(e),o=this.testFilePatterns.some(e=>e.test(a))||this.testFilePatterns.some(t=>t.test(e)&&!t.source.includes("\\.js$")),c=path.relative(this.config.rootPath,e).replace(/\\/g,"/");for(const a of n){if(a.skipInTests&&o)continue;if(a.skipInDirs&&a.skipInDirs.some(e=>c.startsWith(e+"/")||c.includes("/"+e+"/")))continue;if(a.context&&!a.lineContext&&!a.context.test(t))continue;let n=0;for(const o of s){n++;const s=o.match(a.pattern);if(s){if(a.skipIfSafe&&this.safePatterns.some(e=>e.test(o)))continue;if(a.check&&!a.check(s,t,e))continue;r.push({file:e,line:n,column:s.index,category:i,type:a.type||i,severity:a.severity,id:a.id,name:a.name,message:a.message,code:o.trim().substring(0,100)})}}}return r}_runAstScan(e,t,s){let n;try{n=astAnalyzer.analyze(e,t)}catch(e){return[]}if(!n||0===n.length)return[];const i=new Set(s.map(e=>`${e.line}:${e.type}`));return n.filter(e=>!i.has(`${e.line}:${e.type}`))}_buildExportTaintMap(e){try{return taintTracker.buildExportTaintMap(e)}catch(e){return new Map}}_runTaintScan(e,t,s){let n;try{const s=path.extname(e).toLowerCase();if(new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs"]).has(s)){let s;if(this._exportTaintMap){const n=taintTracker._internal.parseJS(t);n&&(s=taintTracker.buildImportSeeds(e,n,this._exportTaintMap,fs.existsSync))}n=taintTracker.analyzeJSTaint(e,t,s&&s.size?s:void 0)}else n=taintTracker.analyze(e,t)}catch(e){return[]}return n&&0!==n.length?n:[]}_runCfgScan(e,t,s){let n;try{n=cfgAnalyzer.analyze(e,t)}catch(e){return[]}if(!n||0===n.length)return[];const i=new Set(s.map(e=>`${e.line}:${e.type}`));return n.filter(e=>!i.has(`${e.line}:${e.type}`))}_analyzeComplexity(e,t,s){const n=[],i=t.match(/function\s+\w+|=>\s*\{|async\s+\w+/g)||[],r=t.match(/\b(if|else|for|while|switch|case|catch|&&|\|\||\?)\b/g)||[],a=i.length>0?r.length/i.length:0;return a>this.config.complexityThreshold&&n.push({file:e,category:"quality",severity:"warning",id:"CMPLX001",name:"High Complexity",message:`Average cyclomatic complexity is ${a.toFixed(1)} (threshold: ${this.config.complexityThreshold})`}),s.length>500&&n.push({file:e,category:"quality",severity:"info",id:"CMPLX002",name:"Long File",message:`File has ${s.length} lines - consider splitting`}),n}_analyzeNesting(e,t,s){const n=[];let i=0,r=0,a=0,o=0;for(const e of s)o++,r+=(e.match(/\{/g)||[]).length-(e.match(/\}/g)||[]).length,r>i&&(i=r,a=o);return i>this.config.nestingThreshold&&n.push({file:e,line:a,category:"quality",severity:"warning",id:"NEST001",name:"Deep Nesting",message:`Max nesting depth is ${i} at line ${a} (threshold: ${this.config.nestingThreshold})`}),n}_scanJson(e,t){const s=[];try{JSON.parse(t)}catch(t){s.push({file:e,category:"quality",severity:"error",id:"JSON001",name:"Invalid JSON",message:`JSON parse error: ${t.message}`})}return/(password|secret|api[_-]?key|token)/i.test(t)&&s.push({file:e,category:"security",severity:"warning",id:"JSON002",name:"Sensitive Data in JSON",message:"Possible sensitive data in JSON file - verify not committed"}),s}_checkDependencyExists(e,t){const s=e[0].match(/require\(['"]([^'"]+)['"]\)/);if(!s)return!0;const n=s[1];if(n.startsWith("."))return!1;if(["fs","path","os","crypto","http","https","events","util","stream","child_process","url","querystring","buffer"].includes(n.split("/")[0]))return!1;try{let e=path.join(this.config.rootPath,"package.json");if(t.includes("server")){const t=path.join(this.config.rootPath,"server","package.json");fs.existsSync(t)&&(e=t)}const s=JSON.parse(fs.readFileSync(e,"utf8")),i={...s.dependencies||{},...s.devDependencies||{}};return!i[n.split("/")[0]]&&!i[n]}catch(e){return!1}}_checkImportUsage(e,t){const s=e[1];if(!s)return!1;const n=new RegExp("\\b"+s+"\\b","g");return(t.match(n)||[]).length<=1}_summarizeIssues(e){return{total:e.length,bySeverity:{critical:e.filter(e=>"critical"===e.severity).length,error:e.filter(e=>"error"===e.severity).length,warning:e.filter(e=>"warning"===e.severity).length,info:e.filter(e=>"info"===e.severity).length},byCategory:e.reduce((e,t)=>(e[t.category]=(e[t.category]||0)+1,e),{}),topFiles:this._getTopFiles(e,10)}}_getTopFiles(e,t){const s=e.reduce((e,t)=>(e[t.file]=(e[t.file]||0)+1,e),{});return Object.entries(s).sort((e,t)=>t[1]-e[1]).slice(0,t).map(([e,t])=>({file:path.relative(this.config.rootPath,e),count:t}))}}module.exports=CodeScanner;
@@ -0,0 +1 @@
1
+ const fs=require("fs"),path=require("path"),crypto=require("crypto");class CodebasePassport{constructor(e={}){this.rootPath=e.rootPath||process.cwd()}generate(e,t={}){const s=Date.now(),r=[],i={},n={},o=[],c=[];let a=0;const l={},u=new Set;for(const t of e){let s;try{if(fs.statSync(t).size>1048576)continue;s=fs.readFileSync(t,"utf-8")}catch(e){continue}const d=path.relative(this.rootPath,t),p=s.split("\n");a+=p.length;const h=path.extname(t);l[h]=(l[h]||0)+p.length,this._detectFrameworks(s,u);const m=this._extractDependencies(s,d),f=this._extractExports(s,d);i[d]=m,f.length>0&&(n[d]=f),this._isEntryPoint(d,s)&&o.push(d);const g=e.filter(e=>{try{const t=fs.readFileSync(e,"utf-8");return t.includes(d.replace(/\\/g,"/"))||t.includes(d.replace(/\//g,"\\"))}catch(e){return!1}}).length;g>5&&c.push({file:d,importedBy:g,risk:g>10?"critical":"high",note:`Changing this file affects ${g} other files. Proceed with caution.`});const y=this._detectPurpose(d,s);r.push({path:d,lines:p.length,purpose:y,imports:m.length,exports:f.length})}const d=this._findCriticalPaths(i),p=this._detectArchitecture(r);return{_version:"1.0.0",_generated:(new Date).toISOString(),_generator:"thuban",project:{name:path.basename(this.rootPath),rootPath:this.rootPath,totalFiles:e.length,totalLines:a,languages:Object.entries(l).sort((e,t)=>t[1]-e[1]).map(([e,t])=>({extension:e,lines:t,percentage:Math.round(t/a*100)})),frameworks:[...u],architecture:p,entryPoints:o},files:r.sort((e,t)=>t.imports-e.imports).slice(0,200),sacred:c.sort((e,t)=>t.importedBy-e.importedBy),dependencies:{totalChains:Object.keys(i).length,criticalPaths:d.slice(0,20),circularRisks:this._findCircularRisks(i)},onboarding:{estimatedReadTimeMinutes:Math.ceil(a/200),startHere:o.slice(0,5),keyFiles:c.slice(0,10).map(e=>e.file),doNotTouch:c.filter(e=>"critical"===e.risk).map(e=>e.file),quickSummary:this._generateQuickSummary(r,u,p)},generatedIn:Date.now()-s+"ms"}}save(e,t){const s=t||path.join(this.rootPath,".thuban-passport.json");return fs.writeFileSync(s,JSON.stringify(e,null,2),"utf-8"),s}_extractDependencies(e,t){const s=[],r=[/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,/import\s+.*?from\s+['"]([^'"]+)['"]/g,/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g];for(const t of r){let r;for(;null!==(r=t.exec(e));)s.push(r[1])}return[...new Set(s)]}_extractExports(e,t){const s=[];e.match(/module\.exports\s*=\s*(\{[\s\S]*?\}|[a-zA-Z_$]\w*)/)&&s.push("module.exports");const r=e.match(/exports\.([a-zA-Z_$]\w*)\s*=/g);r&&r.forEach(e=>s.push(e.replace(/\s*=\s*$/,"")));const i=e.match(/export\s+(?:default\s+)?(?:const|let|var|function|class)\s+([a-zA-Z_$]\w*)/g);return i&&i.forEach(e=>s.push(e)),s}_isEntryPoint(e,t){return!!["index.","main.","app.","server.","cli.","bin/"].some(t=>e.includes(t))||!!t.includes("#!/usr/bin/env")||!(!t.includes("createServer")&&!t.includes(".listen("))}_detectPurpose(e,t){const s=e.toLowerCase();return/route/i.test(s)?"routing":/middleware/i.test(s)?"middleware":/model/i.test(s)?"data model":/controller/i.test(s)?"controller":/service/i.test(s)?"service":/util|helper|lib/i.test(s)?"utility":/config/i.test(s)?"configuration":/test|spec/i.test(s)?"test":/component/i.test(s)?"UI component":/hook/i.test(s)?"React hook":/store|redux|state/i.test(s)?"state management":/api/i.test(s)?"API layer":/db|database|migration/i.test(s)?"database":/auth/i.test(s)?"authentication":/cli|command/i.test(s)?"CLI":t.includes("app.get(")||t.includes("router.")?"routing":t.includes("mongoose.model")||t.includes("Schema")?"data model":t.includes("React")||t.includes("jsx")?"React component":"application logic"}_detectFrameworks(e,t){(e.includes("require('express'")||e.includes("from 'express'"))&&t.add("Express"),(e.includes("require('react'")||e.includes("from 'react'"))&&t.add("React"),(e.includes("require('next'")||e.includes("from 'next'"))&&t.add("Next.js"),(e.includes("require('mongoose'")||e.includes("from 'mongoose'"))&&t.add("Mongoose"),(e.includes("require('fastify'")||e.includes("from 'fastify'"))&&t.add("Fastify"),(e.includes("require('vue'")||e.includes("from 'vue'"))&&t.add("Vue"),(e.includes("require('stripe'")||e.includes("from 'stripe'"))&&t.add("Stripe"),e.includes("@angular")&&t.add("Angular")}_detectArchitecture(e){const t=e.map(e=>e.path.toLowerCase());return t.some(e=>e.includes("controller"))&&t.some(e=>e.includes("model"))?"MVC":t.some(e=>e.includes("service"))&&t.some(e=>e.includes("repository"))?"Service-Repository":t.some(e=>e.includes("component"))&&t.some(e=>e.includes("store"))?"Component-Store (SPA)":t.some(e=>e.includes("pages/"))&&t.some(e=>e.includes("api/"))?"Next.js (Pages Router)":t.some(e=>e.includes("app/"))&&t.some(e=>e.includes("route"))?"App Router":t.some(e=>e.includes("packages/"))?"Monorepo":"Flat"}_findCriticalPaths(e){return Object.entries(e).filter(([,e])=>e.length>3).map(([e,t])=>({file:e,dependsOn:t.length,topDeps:t.slice(0,5)})).sort((e,t)=>t.dependsOn-e.dependsOn).slice(0,20)}_findCircularRisks(e){const t=[];for(const[s,r]of Object.entries(e))for(const i of r){const r=this._resolveRelative(s,i);r&&e[r]&&e[r].some(e=>this._resolveRelative(r,e)===s)&&t.push({fileA:s,fileB:r,risk:"circular dependency"})}return t.slice(0,10)}_resolveRelative(e,t){if(t.startsWith(".")){const s=path.dirname(e);let r=path.join(s,t).replace(/\\/g,"/");return r.match(/\.\w+$/)||(r+=".js"),r}return null}_generateQuickSummary(e,t,s){return`${e.length}-file ${s} project using ${[...t].join(", ")||"vanilla"}. Start with entry points listed above.`}}module.exports=CodebasePassport;
@@ -0,0 +1 @@
1
+ const fs=require("fs"),path=require("path"),crypto=require("crypto");class CopyPasteDriftDetector{constructor(t={}){this.rootPath=t.rootPath||process.cwd(),this.minLines=t.minLines||5,this.similarityThreshold=t.similarity||.7}scan(t){const e=[];for(const n of t){let t;try{if(fs.statSync(n).size>524288)continue;t=fs.readFileSync(n,"utf-8")}catch(t){continue}const s=path.relative(this.rootPath,n);if(this._isSkippable(s))continue;const i=this._extractFunctions(t,s);e.push(...i)}const n=this._findClusters(e),s=n.reduce((t,e)=>t+e.members.reduce((t,e)=>t+e.lineCount,0)-e.members[0].lineCount,0);return{clusters:n,totalClusters:n.length,totalDuplicateInstances:n.reduce((t,e)=>t+e.members.length,0),totalDuplicateLines:s,recommendation:n.length>0?`Extract ${n.length} shared utilities to eliminate ${s} duplicate lines.`:"No copy-paste drift detected."}}_extractFunctions(t,e){const n=t.split("\n"),s=[];for(let t=0;t<n.length;t++){const i=this._detectFunction(n[t]);if(!i)continue;const r=this._findFunctionEnd(n,t),a=n.slice(t,r+1).join("\n"),o=r-t+1;if(o<this.minLines)continue;const l=this._normalize(a),c=crypto.createHash("md5").update(l).digest("hex"),h=this._tokenize(l);s.push({file:e,name:i.name,line:t+1,lineCount:o,hash:c,tokens:h,normalized:l}),t=r}return s}_findClusters(t){const e=new Set,n=[];for(let s=0;s<t.length;s++){if(e.has(s))continue;const i=[t[s]];e.add(s);for(let n=s+1;n<t.length;n++){if(e.has(n))continue;if(t[s].hash===t[n].hash){i.push(t[n]),e.add(n);continue}const r=this._tokenSimilarity(t[s].tokens,t[n].tokens);r>=this.similarityThreshold&&(i.push({...t[n],similarity:Math.round(100*r)}),e.add(n))}if(i.length>=2){const t=this._findDiffs(i);n.push({pattern:i[0].name,members:i.map(t=>({file:t.file,name:t.name,line:t.line,lineCount:t.lineCount,similarity:t.similarity||100})),totalInstances:i.length,differences:t,severity:i.length>=4?"high":i.length>=3?"medium":"low",recommendation:`Extract to shared utility. ${i.length} functions → 1.`})}}return n.sort((t,e)=>e.totalInstances-t.totalInstances)}_normalize(t){const e=[],n=/(?:const|let|var|function)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;let s;for(;null!==(s=n.exec(t));)e.includes(s[1])||e.push(s[1]);let i=t.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").replace(/'[^']*'/g,"STR").replace(/"[^"]*"/g,"STR").replace(/`[^`]*`/g,"STR").replace(/\b\d+\.?\d*\b/g,"NUM");return e.forEach((t,e)=>{const n=new RegExp(`\\b${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`,"g");i=i.replace(n,`VAR_${e}`)}),i.replace(/\s+/g," ").trim()}_tokenize(t){return t.split(/[\s{}()\[\];,=+\-*/<>!&|?:.]+/).filter(t=>t.length>0)}_tokenSimilarity(t,e){if(0===t.length&&0===e.length)return 1;if(0===t.length||0===e.length)return 0;const n=new Set(t),s=new Set(e),i=[...n].filter(t=>s.has(t)).length,r=new Set([...t,...e]).size;return.7*(r>0?i/r:0)+Math.min(t.length,e.length)/Math.max(t.length,e.length)*.3}_findDiffs(t){if(t.length<2)return[];const e=[],n=t[0].normalized;for(let s=1;s<t.length;s++){const i=t[s].normalized;if(n!==i){const r=new Set(this._tokenize(n)),a=new Set(this._tokenize(i)),o=[...a].filter(t=>!r.has(t)),l=[...r].filter(t=>!a.has(t));(o.length>0||l.length>0)&&e.push({file:t[s].file,addedTokens:o.slice(0,5),removedTokens:l.slice(0,5)})}}return e}_detectFunction(t){const e=t.trim();let n=e.match(/^(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);return n?{name:n[1]}:(n=e.match(/^(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s+)?(?:function|\()/),n?{name:n[1]}:(n=e.match(/^(?:private\s+|public\s+|internal\s+|protected\s+)?(?:suspend\s+)?fun\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[(<]/),n?{name:n[1]}:(n=e.match(/^(?:pub(?:\([^)]*\))?\s+)?(?:async\s+)?fn\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*[(<]/),n?{name:n[1]}:(n=e.match(/^(?:(?:public|private|protected|static)\s+)*function\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/),n?{name:n[1]}:(n=e.match(/^def\s+(?:self\.)?([a-zA-Z_][a-zA-Z0-9_?!]*)\s*[(\s]?/),n?{name:n[1]}:null)))))}_findFunctionEnd(t,e){let n=0,s=!1;for(let i=e;i<t.length&&i<e+200;i++){for(const e of t[i])"{"===e&&(n++,s=!0),"}"===e&&n--;if(s&&n<=0)return i}return Math.min(e+20,t.length-1)}_isSkippable(t){return[/\.test\./i,/\.spec\./i,/test[\/\\]/i,/node_modules/i,/\.min\./i,/\.d\.ts$/,/\.config\./i].some(e=>e.test(t))}}module.exports=CopyPasteDriftDetector;
@@ -0,0 +1 @@
1
+ const fs=require("fs"),path=require("path"),EventEmitter=require("events");class DependencyGraph extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),ignorePatterns:e.ignorePatterns||["node_modules",".git","dist",".vite",".orion/snapshots"],extensions:e.extensions||[".js",".ts",".jsx",".tsx",".php",".rb"],...e},this.graph=new Map,this.resolveCache=new Map,this.stats={filesAnalyzed:0,totalImports:0,unresolvedImports:0,circularDependencies:[]}}get circularDeps(){return this.stats.circularDependencies}async build(){Date.now();this.graph.clear(),this.resolveCache.clear(),this.stats={filesAnalyzed:0,totalImports:0,unresolvedImports:0,circularDependencies:[]};const e=await this._discoverFiles(this.config.rootPath);for(const t of e)await this._analyzeFile(t);return this._buildReverseDependencies(),this._detectCircularDependencies(),Date.now(),this.getStats()}getDependencies(e){const t=this._normalizePath(e),s=this.graph.get(t);return s?s.imports:[]}getDependents(e){const t=this._normalizePath(e),s=this.graph.get(t);return s?s.importedBy:[]}getFullDependencyChain(e,t=new Set){const s=this._normalizePath(e);if(t.has(s))return[];t.add(s);const i=this.getDependencies(s),r=[...i];for(const e of i)e.resolved&&r.push(...this.getFullDependencyChain(e.resolved,t));return r}getImpactAnalysis(e){const t=this._normalizePath(e),s={file:t,relativePath:path.relative(this.config.rootPath,t),directDependents:[],indirectDependents:[],totalImpact:0,riskLevel:"low",affectedModules:new Set};s.directDependents=this.getDependents(t).map(e=>({file:e,relativePath:path.relative(this.config.rootPath,e)}));const i=new Set([t]),r=[...s.directDependents.map(e=>e.file)];for(;r.length>0;){const e=r.shift();if(i.has(e))continue;i.add(e);const t=this.getDependents(e);for(const o of t)i.has(o)||(s.indirectDependents.push({file:o,relativePath:path.relative(this.config.rootPath,o),via:path.relative(this.config.rootPath,e)}),r.push(o))}s.totalImpact=s.directDependents.length+s.indirectDependents.length,0===s.totalImpact?s.riskLevel="none":s.totalImpact<=3?s.riskLevel="low":s.totalImpact<=10?s.riskLevel="medium":s.totalImpact<=25?s.riskLevel="high":s.riskLevel="critical";for(const e of[...s.directDependents,...s.indirectDependents]){const t=e.relativePath.split(path.sep);t.length>0&&s.affectedModules.add(t[0])}return s.affectedModules=Array.from(s.affectedModules),s}getMostCriticalFiles(e=20){const t=[];for(const[e,s]of this.graph)t.push({file:e,relativePath:path.relative(this.config.rootPath,e),dependentCount:s.importedBy.length,dependencyCount:s.imports.length});return t.sort((e,t)=>t.dependentCount-e.dependentCount).slice(0,e)}getOrphanFiles(){const e=[];for(const[t,s]of this.graph)0===s.imports.length&&0===s.importedBy.length&&e.push({file:t,relativePath:path.relative(this.config.rootPath,t)});return e}getModuleSummary(){const e=new Map;for(const[t,s]of this.graph){const i=path.relative(this.config.rootPath,t),r=i.split(path.sep)[0];e.has(r)||e.set(r,{name:r,files:[],internalDeps:0,externalDeps:0,dependedOnBy:new Set});const o=e.get(r);o.files.push(i);for(const e of s.imports)e.resolved&&(path.relative(this.config.rootPath,e.resolved).split(path.sep)[0]===r?o.internalDeps++:o.externalDeps++);for(const e of s.importedBy){const t=path.relative(this.config.rootPath,e).split(path.sep)[0];t!==r&&o.dependedOnBy.add(t)}}const t=[];for(const[s,i]of e)t.push({name:s,fileCount:i.files.length,internalDeps:i.internalDeps,externalDeps:i.externalDeps,dependedOnByCount:i.dependedOnBy.size,dependedOnBy:Array.from(i.dependedOnBy)});return t.sort((e,t)=>t.fileCount-e.fileCount)}getStats(){return{...this.stats,totalFiles:this.graph.size,circularCount:this.stats.circularDependencies.length}}toJSON(){const e={generated:(new Date).toISOString(),rootPath:this.config.rootPath,stats:this.getStats(),files:{}};for(const[t,s]of this.graph){const i=path.relative(this.config.rootPath,t);e.files[i]={imports:s.imports.map(e=>({module:e.module,resolved:e.resolved?path.relative(this.config.rootPath,e.resolved):null,specifiers:e.specifiers})),importedBy:s.importedBy.map(e=>path.relative(this.config.rootPath,e))}}return e}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const i=fs.readdirSync(e,{withFileTypes:!0});for(const r of i){const i=path.join(e,r.name);if(!this._shouldIgnore(r.name))if(r.isDirectory())await this._discoverFiles(i,t,s+1);else if(r.isFile()){const e=path.extname(r.name).toLowerCase();this.config.extensions.includes(e)&&t.push(i)}}}catch(e){}return t}_shouldIgnore(e){return this.config.ignorePatterns.some(t=>e===t||e.startsWith(t))}async _analyzeFile(e){try{const t=fs.readFileSync(e,"utf8"),s=this._extractImports(t,e);this.graph.set(e,{imports:s,importedBy:[]}),this.stats.filesAnalyzed++,this.stats.totalImports+=s.length,this.stats.unresolvedImports+=s.filter(e=>!e.resolved).length}catch(e){}}_extractImports(e,t){const s=[],i=path.dirname(t),r=/(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;let o;for(;null!==(o=r.exec(e));){const e=o[1]?o[1].split(",").map(e=>e.trim().split(":")[0].trim()).filter(Boolean):[o[2]],t=o[3];s.push({module:t,specifiers:e,resolved:this._resolveModule(t,i),type:"require"})}const n=/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;for(;null!==(o=n.exec(e));){const e=o[1];s.some(t=>t.module===e)||s.push({module:e,specifiers:[],resolved:this._resolveModule(e,i),type:"require"})}const a=/import\s+(?:(?:\{([^}]+)\}|(\w+)|\*\s+as\s+(\w+))(?:\s*,\s*)?)+\s+from\s+['"]([^'"]+)['"]/g;for(;null!==(o=a.exec(e));){const e=[];o[1]&&e.push(...o[1].split(",").map(e=>e.trim().split(" as ")[0].trim()).filter(Boolean)),o[2]&&e.push(o[2]),o[3]&&e.push(o[3]);const t=o[4];s.push({module:t,specifiers:e,resolved:this._resolveModule(t,i),type:"import"})}return s}_resolveModule(e,t){const s=`${t}:${e}`;if(this.resolveCache.has(s))return this.resolveCache.get(s);let i=null;if(!e.startsWith(".")&&!e.startsWith("/"))return this.resolveCache.set(s,null),null;const r=path.resolve(t,e),o=["",".js",".ts",".jsx",".tsx",".php",".rb","/index.js","/index.ts"];for(const e of o){const t=r+e;if(fs.existsSync(t)&&fs.statSync(t).isFile()){i=t;break}}return this.resolveCache.set(s,i),i}_buildReverseDependencies(){for(const[e,t]of this.graph)for(const s of t.imports)if(s.resolved&&this.graph.has(s.resolved)){const t=this.graph.get(s.resolved);t.importedBy.includes(e)||t.importedBy.push(e)}}_detectCircularDependencies(){const e=new Set,t=new Set,s=(i,r=[])=>{if(t.has(i)){const e=r.indexOf(i),t=r.slice(e).map(e=>path.relative(this.config.rootPath,e));t.push(path.relative(this.config.rootPath,i));const s=t.sort().join(" -> ");return void(this.stats.circularDependencies.some(e=>e.sort().join(" -> ")===s)||this.stats.circularDependencies.push(t))}if(e.has(i))return;e.add(i),t.add(i);const o=this.graph.get(i);if(o)for(const e of o.imports)e.resolved&&s(e.resolved,[...r,i]);t.delete(i)};for(const e of this.graph.keys())s(e)}_normalizePath(e){return path.isAbsolute(e)?e:path.resolve(this.config.rootPath,e)}}module.exports=DependencyGraph;
@@ -0,0 +1 @@
1
+ const fs=require("fs"),path=require("path"),EventEmitter=require("events");class DriftDetector extends EventEmitter{constructor(e={}){super(),this.config={rootPath:e.rootPath||process.cwd(),widgetPattern:e.widgetPattern||/\/\*\*[\s\S]*?@(purpose|module|layer|imports-from|exports-to|depends-on|critical-for)[\s\S]*?\*\//,ignorePatterns:e.ignorePatterns||["node_modules/**",".git/**","dist/**",".orion/snapshots/**"],...e},this.analysisCache=new Map}async detectAll(){const e=Date.now(),t={filesAnalyzed:0,filesWithWidgets:0,filesWithoutWidgets:0,issues:[],moduleMap:{},duration:0};try{const s=await this._discoverFiles(this.config.rootPath);for(const e of s){const s=await this.analyzeFile(e);t.filesAnalyzed++,s.hasWidget?t.filesWithWidgets++:(t.filesWithoutWidgets++,t.issues.push({file:e,category:"drift",severity:"info",id:"DRIFT001",name:"Missing Widget",message:"File has no widget declaration - add @purpose, @module, @layer etc."})),t.issues.push(...s.issues),s.module&&(t.moduleMap[s.module]||(t.moduleMap[s.module]=[]),t.moduleMap[s.module].push(e))}return t.issues.push(...this._analyzeCrossFile(t.moduleMap)),t.duration=Date.now()-e,t}catch(e){return console.error("[DRIFT-DETECTOR] Detection failed"),t.error="Detection failed",t}}async analyzeFile(e){const t={file:e,hasWidget:!1,widget:null,actualExports:[],actualImports:[],module:null,issues:[]};try{const s=fs.readFileSync(e,"utf8");return t.widget=this._parseWidget(s),t.hasWidget=!!t.widget,t.actualExports=this._extractExports(s),t.actualImports=this._extractImports(s),t.widget&&(t.module=t.widget.module,t.issues.push(...this._compareWidgetToCode(e,t.widget,{exports:t.actualExports,imports:t.actualImports}))),t.drifts=t.issues,this.analysisCache.set(e,t),t}catch(s){const i="ENOENT"===s.code?"File not found":"EACCES"===s.code?"Permission denied":"Analysis error";return t.issues.push({file:e,category:"drift",severity:"error",id:"DRIFT000",message:`Failed to analyze file: ${i}`}),t.drifts=t.issues,t}}async checkFileDrift(e){const t=await this.analyzeFile(e);return{hasDrift:t.issues.length>0,issues:t.issues,widget:t.widget}}async suggestWidget(e){try{const t=fs.readFileSync(e,"utf8"),s=this._extractExports(t),i=this._extractImports(t),o=path.relative(this.config.rootPath,e),r=o.split(path.sep)[0];let n="application";return o.includes("routes")||o.includes("api")?n="presentation":o.includes("core")||o.includes("engine")?n="infrastructure":(o.includes("model")||o.includes("domain"))&&(n="domain"),{purpose:s.length>0?`Provides ${s.join(", ")}`:"Unknown purpose - needs description",module:r,layer:n,importsFrom:i.map(e=>`${e.module} - ${e.specifiers.join(", ")}`),exportsTo:"To be determined",dataFlow:"To be documented",sideEffects:t.includes("fs.")?"File system operations":"None documented",criticalFor:"To be documented"}}catch(e){return{error:e.message}}}async _discoverFiles(e,t=[],s=0){if(s>100)return t;try{const i=fs.readdirSync(e,{withFileTypes:!0});for(const o of i){const i=path.join(e,o.name),r=path.relative(this.config.rootPath,i);this._shouldIgnore(r)||(o.isDirectory()?await this._discoverFiles(i,t,s+1):o.isFile()&&o.name.endsWith(".js")&&t.push(i))}}catch(e){}return t}_shouldIgnore(e){const t=e.replace(/\\/g,"/"),s=["node_modules",".git","dist",".vite"];for(const e of s)if(t.includes("/"+e+"/")||t.startsWith(e+"/")||t===e||t.endsWith("/"+e))return!0;for(const e of this.config.ignorePatterns){const s=e.replace(/\\/g,"/"),i=s.split("/")[0].replace(/\*/g,"");if(i&&(t===i||t.startsWith(i+"/")))return!0;let o=s.replace(/\./g,"\\.").replace(/\*\*/g,".*").replace(/\*/g,"[^/]*");if(new RegExp("^"+o).test(t))return!0}return!1}_parseWidget(e){const t=e.match(/\/\*\*[\s\S]*?\*\//);if(!t)return null;const s=t[0],i={},o=["purpose","module","layer","imports-from","exports-to","data-flow","side-effects","depends-on-env","critical-for","known-issues","last-audit","last-modified"];for(const e of o){const t=s.match(new RegExp(`@${e}\\s+(.+?)(?=\\n\\s*\\*\\s*@|\\n\\s*\\*\\/|$)`,"s"));t&&(i[e.replace(/-/g,"_")]=t[1].trim().replace(/\n\s*\*\s*/g," ").trim())}return Object.keys(i).length>0?i:null}_extractExports(e){const t=[],s=e.match(/module\.exports\s*=\s*\{([^}]+)\}/);if(s){const e=s[1].match(/\w+/g)||[];t.push(...e)}const i=e.match(/module\.exports\s*=\s*(class\s+)?(\w+)/);i&&i[2]&&t.push(i[2]);const o=e.match(/exports\.(\w+)\s*=/g)||[];for(const e of o){const s=e.match(/exports\.(\w+)/);s&&t.push(s[1])}return[...new Set(t)]}_extractImports(e){const t=[],s=e.matchAll(/(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\(['"]([^'"]+)['"]\)/g);for(const e of s){const s=e[1]?e[1].split(",").map(e=>e.trim().split(":")[0].trim()):[e[2]];t.push({module:e[3],specifiers:s.filter(Boolean)})}return t}_compareWidgetToCode(e,t,s){const i=[];if(t.exports_to){const o=t.exports_to.split(",").map(e=>e.trim().split(" ")[0]).filter(e=>!s.exports.some(t=>t.toLowerCase().includes(e.toLowerCase())));o.length>0&&i.push({file:e,category:"drift",severity:"warning",id:"DRIFT002",name:"Missing Declared Export",message:`Widget declares exports not found in code: ${o.join(", ")}`})}if(t.imports_from){const o=t.imports_from.split(",").map(e=>e.trim().split(" ")[0]),r=s.imports.map(e=>e.module),n=o.filter(e=>!r.some(t=>t.includes(e)||e.includes(t.split("/").pop())));n.length>0&&i.push({file:e,category:"drift",severity:"warning",id:"DRIFT003",name:"Missing Declared Import",message:`Widget declares imports not found in code: ${n.join(", ")}`})}return!/fs\.(write|append|unlink|mkdir|rmdir)/i.test(fs.readFileSync(e,"utf8"))||t.side_effects&&"None"!==t.side_effects||i.push({file:e,category:"drift",severity:"warning",id:"DRIFT004",name:"Undeclared Side Effects",message:"File has file system operations but widget declares no side effects"}),i}_analyzeCrossFile(e){const t=[];for(const[s,i]of Object.entries(e))1===i.length&&t.push({file:i[0],category:"drift",severity:"info",id:"DRIFT005",name:"Orphaned Module",message:`Module "${s}" only has one file - verify module organization`});return t}}module.exports=DriftDetector;
@@ -0,0 +1 @@
1
+ const fs=require("fs"),path=require("path"),crypto=require("crypto");class ExecutiveReport{constructor(n={}){this.rootPath=n.rootPath||process.cwd(),this.companyName=n.companyName||this._detectProjectName(),this.generatedAt=new Date}_detectProjectName(){try{return JSON.parse(fs.readFileSync(path.join(this.rootPath,"package.json"),"utf-8")).name||path.basename(this.rootPath)}catch{return path.basename(this.rootPath)}}generate(n){const{scanResults:e={},hallResults:t={},debtCost:o={},ghostResults:s={},aiScoreResults:a={},cloneResults:r={},passportData:i={},fileCount:l=0,lineCount:d=0}=n,c=`THB-${Date.now().toString(36).toUpperCase()}`,h=this._calculateOverallScore(n),m=this._scoreToGrade(h),g=this._gradeColor(m);return this._renderHTML({reportId:c,score:h,grade:m,gradeColor:g,scanResults:e,hallResults:t,debtCost:o,ghostResults:s,aiScoreResults:a,cloneResults:r,passportData:i,fileCount:l,lineCount:d})}_calculateOverallScore(n){let e=100;const t=n.hallResults||{},o=n.ghostResults||{},s=n.cloneResults||{},a=n.aiScoreResults||{},r=n.scanResults||{};e-=15*(t.phantomAPIs?.length||0),e-=5*(t.deprecatedAPIs?.length||0),e-=2*(o.totalGhosts||0),e-=3*(s.totalClusters||0),e-=1*(a.aiLikelyCount||0);let i=0;if(r&&"object"==typeof r)for(const[,n]of Object.entries(r))n?.issues&&(i+=n.issues.length);return e-=Math.min(.5*i,30),Math.max(0,Math.min(100,Math.round(e)))}_scoreToGrade(n){return n>=90?"A":n>=80?"B":n>=65?"C":n>=50?"D":"F"}_gradeColor(n){return{A:"#00ff88",B:"#88ff00",C:"#ffd93d",D:"#ff8844",F:"#ff4444"}[n]||"#ff4444"}_renderHTML(n){const e=this.generatedAt,t=e.toLocaleDateString("en-GB",{day:"numeric",month:"long",year:"numeric"}),o=e.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit"}),s=n.hallResults.phantomAPIs?.length||0,a=n.hallResults.deprecatedAPIs?.length||0,r=n.ghostResults.totalGhosts||0,i=n.ghostResults.totalWastedLines||0,l=n.cloneResults.totalClusters||0,d=n.cloneResults.totalDuplicateLines||0,c=n.aiScoreResults.aiLikelyCount||0,h=n.aiScoreResults.aiPossibleCount||0,m=n.aiScoreResults.aiPercentage||0,g=n.debtCost.totalHoursManual||0,v=n.debtCost.totalCostManual||0,u=n.debtCost.totalHoursThuban||0,p=n.debtCost.totalSaving||0,f=n.debtCost.savingPercent||0,b=n.debtCost.weeklyDebtAccumulation||.7,y=Math.round(4*b*80);let x="";if(n.hallResults.phantomAPIs)for(const e of n.hallResults.phantomAPIs.slice(0,8))x+=`<tr>\n <td class="sev-critical">CRITICAL</td>\n <td>${this._esc(e.file||"")}:${e.line||"?"}</td>\n <td>${this._esc(e.name||e.code||"Phantom API")}</td>\n <td>Will crash at runtime — method does not exist</td>\n </tr>`;if(n.hallResults.deprecatedAPIs)for(const e of n.hallResults.deprecatedAPIs.slice(0,5))x+=`<tr>\n <td class="sev-high">HIGH</td>\n <td>${this._esc(e.file||"")}:${e.line||"?"}</td>\n <td>${this._esc(e.name||e.code||"Deprecated API")}</td>\n <td>Deprecated — will break in future versions</td>\n </tr>`;let $="";if(n.ghostResults.ghosts)for(const e of n.ghostResults.ghosts.slice(0,8))$+=`<tr>\n <td>${this._esc(e.file)}:${e.line}</td>\n <td>${this._esc(e.name)}()</td>\n <td>${e.wastedLines} lines</td>\n <td>${e.references} references</td>\n </tr>`;let w="";if(n.aiScoreResults.highConfidence)for(const e of n.aiScoreResults.highConfidence.slice(0,8))w+=`<tr>\n <td class="sev-critical">${e.confidence}%</td>\n <td>${this._esc(e.file)}:${e.line}</td>\n <td>${this._esc(e.name)}()</td>\n <td>${e.lineCount} lines</td>\n </tr>`;let C="";if(n.cloneResults.clusters)for(const e of n.cloneResults.clusters.slice(0,6)){const n=e.members.map(n=>`${this._esc(n.file)}:${n.line}`).join("<br>");C+=`<tr>\n <td>${this._esc(e.pattern)}</td>\n <td>${e.totalInstances}</td>\n <td style="font-size:0.75rem">${n}</td>\n <td>${this._esc(e.recommendation)}</td>\n </tr>`}return`<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<meta name="viewport" content="width=device-width, initial-scale=1.0">\n<title>Thuban Code Health Report — ${this._esc(this.companyName)}</title>\n<style>\n @page { size: A4; margin: 15mm; }\n @media print {\n body { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }\n .no-print { display: none !important; }\n .page-break { page-break-before: always; }\n }\n\n :root {\n --bg: #0a0a1a;\n --surface: #12122a;\n --border: #2a2a5a;\n --text: #e0e0f0;\n --dim: #8888aa;\n --cyan: #00f6ff;\n --green: #00ff88;\n --yellow: #ffd93d;\n --red: #ff4444;\n --mono: 'JetBrains Mono', 'Cascadia Code', 'Consolas', monospace;\n --sans: 'Segoe UI', system-ui, -apple-system, sans-serif;\n }\n\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n background: var(--bg);\n color: var(--text);\n font-family: var(--sans);\n font-size: 11pt;\n line-height: 1.5;\n max-width: 210mm;\n margin: 0 auto;\n padding: 20mm 15mm;\n }\n\n /* Header */\n .report-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n border-bottom: 2px solid var(--cyan);\n padding-bottom: 1.5rem;\n margin-bottom: 2rem;\n }\n\n .report-header .logo {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n }\n\n .report-header .logo svg { width: 36px; height: 36px; }\n\n .report-header .logo-text {\n font-size: 1.5rem;\n font-weight: 300;\n letter-spacing: 0.3em;\n color: var(--cyan);\n }\n\n .report-header .meta {\n text-align: right;\n font-size: 0.8rem;\n color: var(--dim);\n line-height: 1.8;\n }\n\n .report-title {\n text-align: center;\n margin-bottom: 2.5rem;\n }\n\n .report-title h1 {\n font-size: 1.8rem;\n font-weight: 700;\n margin-bottom: 0.25rem;\n }\n\n .report-title .project-name {\n color: var(--cyan);\n font-size: 1.1rem;\n font-family: var(--mono);\n }\n\n /* Score Ring */\n .score-section {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 3rem;\n margin-bottom: 2.5rem;\n padding: 2rem;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 12px;\n }\n\n .score-ring {\n position: relative;\n width: 140px;\n height: 140px;\n }\n\n .score-ring svg { transform: rotate(-90deg); }\n\n .score-ring .score-value {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n }\n\n .score-ring .grade {\n font-size: 2.5rem;\n font-weight: 700;\n font-family: var(--mono);\n line-height: 1;\n }\n\n .score-ring .number {\n font-size: 0.85rem;\n color: var(--dim);\n }\n\n .score-summary {\n flex: 1;\n }\n\n .score-summary h2 {\n font-size: 1.3rem;\n margin-bottom: 0.75rem;\n }\n\n .stat-row {\n display: flex;\n gap: 2rem;\n flex-wrap: wrap;\n margin-top: 1rem;\n }\n\n .stat {\n text-align: center;\n min-width: 80px;\n }\n\n .stat .stat-value {\n font-size: 1.5rem;\n font-weight: 700;\n font-family: var(--mono);\n }\n\n .stat .stat-label {\n font-size: 0.7rem;\n color: var(--dim);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n\n .stat-critical .stat-value { color: var(--red); }\n .stat-warning .stat-value { color: var(--yellow); }\n .stat-good .stat-value { color: var(--green); }\n .stat-info .stat-value { color: var(--cyan); }\n\n /* Sections */\n .section {\n margin-bottom: 2rem;\n }\n\n .section-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 1rem;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid var(--border);\n }\n\n .section-header h3 {\n font-size: 1rem;\n font-weight: 600;\n }\n\n .section-header .icon { font-size: 1.2rem; }\n\n /* Tables */\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.8rem;\n margin-bottom: 1rem;\n }\n\n th {\n text-align: left;\n padding: 0.5rem;\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n font-weight: 600;\n font-size: 0.7rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--dim);\n }\n\n td {\n padding: 0.4rem 0.5rem;\n border-bottom: 1px solid rgba(42,42,90,0.3);\n font-family: var(--mono);\n font-size: 0.75rem;\n word-break: break-all;\n }\n\n .sev-critical { color: var(--red); font-weight: 700; }\n .sev-high { color: var(--yellow); font-weight: 600; }\n\n /* Cost Box */\n .cost-box {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 12px;\n padding: 1.5rem 2rem;\n margin-bottom: 1rem;\n }\n\n .cost-grid {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n gap: 1.5rem;\n text-align: center;\n }\n\n .cost-item .cost-value {\n font-size: 1.8rem;\n font-weight: 700;\n font-family: var(--mono);\n }\n\n .cost-item .cost-label {\n font-size: 0.75rem;\n color: var(--dim);\n margin-top: 0.25rem;\n }\n\n .cost-manual .cost-value { color: var(--red); }\n .cost-thuban .cost-value { color: var(--cyan); }\n .cost-saving .cost-value { color: var(--green); }\n\n .roi-bar {\n margin-top: 1.5rem;\n padding: 1rem;\n background: rgba(0,255,136,0.08);\n border: 1px solid rgba(0,255,136,0.2);\n border-radius: 8px;\n text-align: center;\n font-size: 0.85rem;\n }\n\n .roi-bar strong { color: var(--green); }\n\n /* Warning callout */\n .callout {\n padding: 1rem 1.5rem;\n border-radius: 8px;\n margin-bottom: 1rem;\n font-size: 0.85rem;\n }\n\n .callout-danger {\n background: rgba(255,68,68,0.08);\n border: 1px solid rgba(255,68,68,0.2);\n color: #ffaaaa;\n }\n\n .callout-warning {\n background: rgba(255,217,61,0.08);\n border: 1px solid rgba(255,217,61,0.2);\n color: #ffe088;\n }\n\n /* Footer */\n .report-footer {\n margin-top: 3rem;\n padding-top: 1.5rem;\n border-top: 1px solid var(--border);\n display: flex;\n justify-content: space-between;\n font-size: 0.75rem;\n color: var(--dim);\n }\n\n .report-footer a { color: var(--cyan); text-decoration: none; }\n\n /* Print button */\n .print-btn {\n position: fixed;\n bottom: 2rem;\n right: 2rem;\n background: var(--cyan);\n color: var(--bg);\n border: none;\n padding: 0.75rem 1.5rem;\n border-radius: 8px;\n font-weight: 600;\n font-size: 0.9rem;\n cursor: pointer;\n box-shadow: 0 4px 20px rgba(0,246,255,0.3);\n z-index: 100;\n }\n\n .print-btn:hover { background: #33f8ff; }\n\n /* Responsive for screen viewing */\n @media screen and (max-width: 700px) {\n body { padding: 1rem; }\n .cost-grid { grid-template-columns: 1fr; }\n .stat-row { flex-direction: column; gap: 0.75rem; }\n .score-section { flex-direction: column; gap: 1.5rem; }\n }\n</style>\n</head>\n<body>\n\n<button class="print-btn no-print" onclick="window.print()">Save as PDF</button>\n\n\x3c!-- Header --\x3e\n<div class="report-header">\n <div class="logo">\n <svg viewBox="0 0 32 32" fill="none">\n <polygon points="16,2 30,28 2,28" stroke="#00f6ff" stroke-width="2" fill="none"/>\n <polygon points="16,8 25,25 7,25" stroke="#00f6ff" stroke-width="1" fill="rgba(0,246,255,0.1)"/>\n <circle cx="16" cy="18" r="3" fill="#00f6ff"/>\n </svg>\n <span class="logo-text">THUBAN</span>\n </div>\n <div class="meta">\n Report ID: ${n.reportId}<br>\n Generated: ${t} at ${o}<br>\n Engine: Thuban Code Health v0.3.0\n </div>\n</div>\n\n\x3c!-- Title --\x3e\n<div class="report-title">\n <h1>Code Health Report</h1>\n <div class="project-name">${this._esc(this.companyName)}</div>\n</div>\n\n\x3c!-- Score --\x3e\n<div class="score-section">\n <div class="score-ring">\n <svg width="140" height="140" viewBox="0 0 140 140">\n <circle cx="70" cy="70" r="60" fill="none" stroke="${n.gradeColor}22" stroke-width="10"/>\n <circle cx="70" cy="70" r="60" fill="none" stroke="${n.gradeColor}" stroke-width="10"\n stroke-dasharray="${Math.round(3.77*n.score)} 377"\n stroke-linecap="round"/>\n </svg>\n <div class="score-value">\n <div class="grade" style="color:${n.gradeColor}">${n.grade}</div>\n <div class="number">${n.score}/100</div>\n </div>\n </div>\n <div class="score-summary">\n <h2>Codebase Health: ${"A"===n.grade?"Excellent":"B"===n.grade?"Good":"C"===n.grade?"Needs Attention":"D"===n.grade?"At Risk":"Critical"}</h2>\n <div class="stat-row">\n <div class="stat stat-info">\n <div class="stat-value">${n.fileCount.toLocaleString()}</div>\n <div class="stat-label">Files Scanned</div>\n </div>\n <div class="stat stat-info">\n <div class="stat-value">${n.lineCount.toLocaleString()}</div>\n <div class="stat-label">Lines of Code</div>\n </div>\n <div class="stat ${s>0?"stat-critical":"stat-good"}">\n <div class="stat-value">${s}</div>\n <div class="stat-label">Hallucinated APIs</div>\n </div>\n <div class="stat ${r>0?"stat-warning":"stat-good"}">\n <div class="stat-value">${r}</div>\n <div class="stat-label">Ghost Functions</div>\n </div>\n <div class="stat ${c>0?"stat-warning":"stat-good"}">\n <div class="stat-value">${m}%</div>\n <div class="stat-label">AI-Generated</div>\n </div>\n </div>\n </div>\n</div>\n\n${s>0?`\n<div class="callout callout-danger">\n <strong>PRODUCTION RISK:</strong> ${s} API call${s>1?"s":""} in this codebase reference${1===s?"s":""} methods that do not exist.\n These will throw runtime errors in production. This is not tech debt — this is code that <strong>will crash</strong>.\n</div>\n`:""}\n\n\x3c!-- Tech Debt Cost --\x3e\n<div class="section">\n <div class="section-header">\n <span class="icon">&#128176;</span>\n <h3>Technical Debt Cost Analysis</h3>\n </div>\n <div class="cost-box">\n <div class="cost-grid">\n <div class="cost-item cost-manual">\n <div class="cost-value">&pound;${v.toLocaleString()}</div>\n <div class="cost-label">Manual fix cost (${g} dev-hours at &pound;80/hr)</div>\n </div>\n <div class="cost-item cost-thuban">\n <div class="cost-value">${u}h</div>\n <div class="cost-label">Fix time with Thuban auto-fix</div>\n </div>\n <div class="cost-item cost-saving">\n <div class="cost-value">&pound;${p.toLocaleString()}</div>\n <div class="cost-label">You save (${f}%)</div>\n </div>\n </div>\n <div class="roi-bar">\n Every week this debt goes unaddressed, <strong>${b} more hours</strong> accumulate.\n That is <strong>&pound;${y.toLocaleString()}/month</strong> in growing technical debt.\n Thuban Pro costs &pound;19/month. <strong>ROI: ${v>0?Math.round(v/19):0}x.</strong>\n </div>\n </div>\n</div>\n\n${s+a>0?`\n\x3c!-- Hallucinations --\x3e\n<div class="section">\n <div class="section-header">\n <span class="icon">&#128302;</span>\n <h3>AI Hallucination Detection</h3>\n </div>\n <p style="font-size:0.85rem; color:var(--dim); margin-bottom:1rem;">\n These API calls reference methods, modules, or endpoints that do not exist in the libraries being used.\n They were likely generated by an AI coding tool and will fail at runtime.\n </p>\n <table>\n <thead>\n <tr><th>Severity</th><th>Location</th><th>API Call</th><th>Impact</th></tr>\n </thead>\n <tbody>${x}</tbody>\n </table>\n</div>\n`:""}\n\n${r>0?`\n<div class="page-break"></div>\n\x3c!-- Ghost Code --\x3e\n<div class="section">\n <div class="section-header">\n <span class="icon">&#128123;</span>\n <h3>Ghost Code — Dead Functions</h3>\n </div>\n <p style="font-size:0.85rem; color:var(--dim); margin-bottom:1rem;">\n ${r} function${r>1?"s":""} exist in the codebase but are never called.\n This represents ${i.toLocaleString()} wasted lines of code (${n.ghostResults.wastedPercent||"?"}% of the codebase).\n </p>\n <table>\n <thead>\n <tr><th>Location</th><th>Function</th><th>Size</th><th>References</th></tr>\n </thead>\n <tbody>${$}</tbody>\n </table>\n</div>\n`:""}\n\n${c>0?`\n\x3c!-- AI Confidence --\x3e\n<div class="section">\n <div class="section-header">\n <span class="icon">&#129302;</span>\n <h3>AI-Generated Code Confidence</h3>\n </div>\n <p style="font-size:0.85rem; color:var(--dim); margin-bottom:1rem;">\n ${m}% of functions show strong signals of AI generation.\n ${c} high confidence, ${h} possible.\n These functions should be reviewed for correctness, edge cases, and test coverage.\n </p>\n <table>\n <thead>\n <tr><th>AI %</th><th>Location</th><th>Function</th><th>Size</th></tr>\n </thead>\n <tbody>${w}</tbody>\n </table>\n</div>\n`:""}\n\n${l>0?`\n\x3c!-- Copy-Paste Drift --\x3e\n<div class="section">\n <div class="section-header">\n <span class="icon">&#128203;</span>\n <h3>Copy-Paste Drift</h3>\n </div>\n <p style="font-size:0.85rem; color:var(--dim); margin-bottom:1rem;">\n ${l} cluster${l>1?"s":""} of similar code found across ${d} duplicate lines.\n These indicate copy-pasted logic that has drifted apart and should be extracted into shared utilities.\n </p>\n <table>\n <thead>\n <tr><th>Pattern</th><th>Instances</th><th>Locations</th><th>Recommendation</th></tr>\n </thead>\n <tbody>${C}</tbody>\n </table>\n</div>\n`:""}\n\n\x3c!-- Recommendations --\x3e\n<div class="section">\n <div class="section-header">\n <span class="icon">&#9889;</span>\n <h3>Recommended Actions</h3>\n </div>\n <div style="display:grid; grid-template-columns: auto 1fr; gap: 0.5rem 1rem; font-size:0.85rem;">\n ${s>0?`\n <div style="color:var(--red); font-weight:700;">1. IMMEDIATE</div>\n <div>Fix ${s} hallucinated API${s>1?"s":""} — these will crash in production. Run <code style="background:var(--surface);padding:0.1rem 0.4rem;border-radius:4px;font-family:var(--mono);color:var(--cyan);">thuban fix . --fix</code></div>\n `:""}\n ${r>0?`\n <div style="color:var(--yellow); font-weight:700;">${s>0?"2":"1"}. HIGH</div>\n <div>Remove ${r} ghost function${r>1?"s":""} (${i} wasted lines). Run <code style="background:var(--surface);padding:0.1rem 0.4rem;border-radius:4px;font-family:var(--mono);color:var(--cyan);">thuban ghosts .</code> for full list</div>\n `:""}\n ${l>0?`\n <div style="color:var(--yellow); font-weight:700;">${s>0?"3":"2"}. MEDIUM</div>\n <div>Consolidate ${l} copy-paste cluster${l>1?"s":""} into shared utilities</div>\n `:""}\n ${c>0?`\n <div style="color:var(--cyan); font-weight:700;">${s>0?"4":"3"}. REVIEW</div>\n <div>Audit ${c} high-confidence AI-generated function${c>1?"s":""} for correctness and test coverage</div>\n `:""}\n <div style="color:var(--green); font-weight:700;">ONGOING</div>\n <div>Install pre-commit gate: <code style="background:var(--surface);padding:0.1rem 0.4rem;border-radius:4px;font-family:var(--mono);color:var(--cyan);">thuban gate --fix</code> — blocks hallucinated code from entering your repo</div>\n </div>\n</div>\n\n\x3c!-- Footer --\x3e\n<div class="report-footer">\n <div>\n <strong>Thuban</strong> — Code Health Engine<br>\n <a href="https://thuban.dev">thuban.dev</a> &nbsp;|&nbsp; A Silverwings product\n </div>\n <div style="text-align:right;">\n Report: ${n.reportId}<br>\n This report was generated automatically.<br>\n For full details, run: <code style="font-family:var(--mono); color:var(--cyan);">npx thuban scan .</code>\n </div>\n</div>\n\n</body>\n</html>`}_esc(n){return String(n).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;")}save(n,e){const t=e||this.rootPath,o=`thuban-report-${this.generatedAt.toISOString().split("T")[0]}.html`,s=path.join(t,o);return fs.writeFileSync(s,n,"utf-8"),s}}module.exports=ExecutiveReport;