moriarty-project 0.1.6__py3-none-any.whl

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 (416) hide show
  1. moriarty/__init__.py +5 -0
  2. moriarty/adapters/__init__.py +0 -0
  3. moriarty/agent/__init__.py +0 -0
  4. moriarty/assets/modules/.gitkeep +0 -0
  5. moriarty/assets/modules/asia/douban.yaml +19 -0
  6. moriarty/assets/modules/asia/kakao.yaml +19 -0
  7. moriarty/assets/modules/asia/line.yaml +19 -0
  8. moriarty/assets/modules/asia/mixi.yaml +19 -0
  9. moriarty/assets/modules/asia/naver.yaml +19 -0
  10. moriarty/assets/modules/asia/qq.yaml +19 -0
  11. moriarty/assets/modules/asia/vk.yaml +19 -0
  12. moriarty/assets/modules/asia/wechat.yaml +19 -0
  13. moriarty/assets/modules/asia/weibo.yaml +19 -0
  14. moriarty/assets/modules/asia/xiaohongshu.yaml +19 -0
  15. moriarty/assets/modules/behance.yaml +47 -0
  16. moriarty/assets/modules/business/crunchbase.yaml +27 -0
  17. moriarty/assets/modules/business/fiverr.yaml +32 -0
  18. moriarty/assets/modules/business/freelancer.yaml +27 -0
  19. moriarty/assets/modules/business/glassdoor.yaml +27 -0
  20. moriarty/assets/modules/business/guru.yaml +26 -0
  21. moriarty/assets/modules/business/indeed.yaml +25 -0
  22. moriarty/assets/modules/business/monster.yaml +25 -0
  23. moriarty/assets/modules/business/peopleperhour.yaml +26 -0
  24. moriarty/assets/modules/business/toptal.yaml +28 -0
  25. moriarty/assets/modules/business/upwork.yaml +27 -0
  26. moriarty/assets/modules/business/ziprecruiter.yaml +25 -0
  27. moriarty/assets/modules/content/buymeacoffee.yaml +27 -0
  28. moriarty/assets/modules/content/gumroad.yaml +27 -0
  29. moriarty/assets/modules/content/ko-fi.yaml +32 -0
  30. moriarty/assets/modules/content/onlyfans.yaml +27 -0
  31. moriarty/assets/modules/content/patreon.yaml +33 -0
  32. moriarty/assets/modules/content/substack.yaml +32 -0
  33. moriarty/assets/modules/creative/500px.yaml +31 -0
  34. moriarty/assets/modules/creative/artstation.yaml +33 -0
  35. moriarty/assets/modules/creative/deviantart.yaml +32 -0
  36. moriarty/assets/modules/creative/flickr.yaml +31 -0
  37. moriarty/assets/modules/creative/pexels.yaml +26 -0
  38. moriarty/assets/modules/creative/unsplash.yaml +26 -0
  39. moriarty/assets/modules/creative/vimeo.yaml +31 -0
  40. moriarty/assets/modules/crypto/binance.yaml +27 -0
  41. moriarty/assets/modules/crypto/bitcointalk.yaml +33 -0
  42. moriarty/assets/modules/crypto/coinbase.yaml +26 -0
  43. moriarty/assets/modules/crypto/etherscan.yaml +32 -0
  44. moriarty/assets/modules/crypto/foundation.yaml +28 -0
  45. moriarty/assets/modules/crypto/kraken.yaml +27 -0
  46. moriarty/assets/modules/crypto/mirror.yaml +27 -0
  47. moriarty/assets/modules/crypto/niftygateway.yaml +26 -0
  48. moriarty/assets/modules/crypto/opensea.yaml +32 -0
  49. moriarty/assets/modules/crypto/rarible.yaml +27 -0
  50. moriarty/assets/modules/crypto/superrare.yaml +29 -0
  51. moriarty/assets/modules/dating/bumble.yaml +25 -0
  52. moriarty/assets/modules/dating/grindr.yaml +27 -0
  53. moriarty/assets/modules/dating/happn.yaml +25 -0
  54. moriarty/assets/modules/dating/her.yaml +27 -0
  55. moriarty/assets/modules/dating/hinge.yaml +25 -0
  56. moriarty/assets/modules/dating/match.yaml +25 -0
  57. moriarty/assets/modules/dating/meetme.yaml +27 -0
  58. moriarty/assets/modules/dating/okcupid.yaml +25 -0
  59. moriarty/assets/modules/dating/pof.yaml +25 -0
  60. moriarty/assets/modules/dating/tinder.yaml +25 -0
  61. moriarty/assets/modules/dating-nsfw/adultfriendfinder.yaml +28 -0
  62. moriarty/assets/modules/dating-nsfw/ashley-madison.yaml +26 -0
  63. moriarty/assets/modules/design/adobe-portfolio.yaml +27 -0
  64. moriarty/assets/modules/design/carbonmade.yaml +27 -0
  65. moriarty/assets/modules/design/cgsociety.yaml +27 -0
  66. moriarty/assets/modules/design/coroflot.yaml +27 -0
  67. moriarty/assets/modules/design/figma.yaml +27 -0
  68. moriarty/assets/modules/design/sketch.yaml +26 -0
  69. moriarty/assets/modules/dev/bitbucket.yaml +35 -0
  70. moriarty/assets/modules/dev/codeforces.yaml +32 -0
  71. moriarty/assets/modules/dev/codepen.yaml +34 -0
  72. moriarty/assets/modules/dev/hackerone.yaml +32 -0
  73. moriarty/assets/modules/dev/hackthebox.yaml +27 -0
  74. moriarty/assets/modules/dev/huggingface.yaml +27 -0
  75. moriarty/assets/modules/dev/kaggle.yaml +32 -0
  76. moriarty/assets/modules/dev/leetcode.yaml +32 -0
  77. moriarty/assets/modules/dev/replit.yaml +31 -0
  78. moriarty/assets/modules/dribbble.yaml +53 -0
  79. moriarty/assets/modules/ecommerce/etsy.yaml +32 -0
  80. moriarty/assets/modules/education/duolingo.yaml +32 -0
  81. moriarty/assets/modules/education/edx.yaml +26 -0
  82. moriarty/assets/modules/education/khanacademy.yaml +26 -0
  83. moriarty/assets/modules/education/lynda.yaml +27 -0
  84. moriarty/assets/modules/education/memrise.yaml +27 -0
  85. moriarty/assets/modules/education/pluralsight.yaml +27 -0
  86. moriarty/assets/modules/education/skillshare.yaml +27 -0
  87. moriarty/assets/modules/education/udacity.yaml +27 -0
  88. moriarty/assets/modules/email/github_email.yaml +40 -0
  89. moriarty/assets/modules/email/gravatar.yaml +23 -0
  90. moriarty/assets/modules/europe/badoo.yaml +19 -0
  91. moriarty/assets/modules/europe/lovoo.yaml +19 -0
  92. moriarty/assets/modules/europe/myspace.yaml +19 -0
  93. moriarty/assets/modules/europe/netlog.yaml +19 -0
  94. moriarty/assets/modules/europe/ok.yaml +19 -0
  95. moriarty/assets/modules/europe/skyrock.yaml +19 -0
  96. moriarty/assets/modules/europe/studivz.yaml +19 -0
  97. moriarty/assets/modules/europe/tuenti.yaml +19 -0
  98. moriarty/assets/modules/europe/viadeo.yaml +19 -0
  99. moriarty/assets/modules/europe/xing.yaml +19 -0
  100. moriarty/assets/modules/fitness/fitbit.yaml +27 -0
  101. moriarty/assets/modules/fitness/garmin.yaml +27 -0
  102. moriarty/assets/modules/fitness/myfitnesspal.yaml +27 -0
  103. moriarty/assets/modules/fitness/strava.yaml +33 -0
  104. moriarty/assets/modules/fitness/zwift.yaml +28 -0
  105. moriarty/assets/modules/food/allrecipes.yaml +27 -0
  106. moriarty/assets/modules/food/tasty.yaml +27 -0
  107. moriarty/assets/modules/food/yelp.yaml +32 -0
  108. moriarty/assets/modules/food/zomato.yaml +28 -0
  109. moriarty/assets/modules/forums/4chan.yaml +26 -0
  110. moriarty/assets/modules/forums/8kun.yaml +26 -0
  111. moriarty/assets/modules/forums/9gag.yaml +26 -0
  112. moriarty/assets/modules/forums/discourse.yaml +26 -0
  113. moriarty/assets/modules/forums/disqus.yaml +31 -0
  114. moriarty/assets/modules/forums/hackernews.yaml +32 -0
  115. moriarty/assets/modules/forums/launchpad.yaml +27 -0
  116. moriarty/assets/modules/forums/phpbb.yaml +25 -0
  117. moriarty/assets/modules/forums/quora.yaml +32 -0
  118. moriarty/assets/modules/forums/serverfault.yaml +27 -0
  119. moriarty/assets/modules/forums/slashdot.yaml +28 -0
  120. moriarty/assets/modules/forums/stackexchange.yaml +32 -0
  121. moriarty/assets/modules/forums/superuser.yaml +27 -0
  122. moriarty/assets/modules/forums/vbulletin.yaml +25 -0
  123. moriarty/assets/modules/forums/xenforo.yaml +25 -0
  124. moriarty/assets/modules/forums-nsfw/kiwifarms.yaml +25 -0
  125. moriarty/assets/modules/forums-nsfw/lolcow.yaml +26 -0
  126. moriarty/assets/modules/gaming/apextracker.yaml +27 -0
  127. moriarty/assets/modules/gaming/battlenet.yaml +26 -0
  128. moriarty/assets/modules/gaming/chess.yaml +30 -0
  129. moriarty/assets/modules/gaming/discord-public.yaml +27 -0
  130. moriarty/assets/modules/gaming/dotabuff.yaml +32 -0
  131. moriarty/assets/modules/gaming/epicgames.yaml +25 -0
  132. moriarty/assets/modules/gaming/faceit.yaml +33 -0
  133. moriarty/assets/modules/gaming/fortnitetracker.yaml +32 -0
  134. moriarty/assets/modules/gaming/gog.yaml +26 -0
  135. moriarty/assets/modules/gaming/itch.yaml +32 -0
  136. moriarty/assets/modules/gaming/kongregate.yaml +25 -0
  137. moriarty/assets/modules/gaming/minecraft.yaml +31 -0
  138. moriarty/assets/modules/gaming/opgg.yaml +32 -0
  139. moriarty/assets/modules/gaming/origin.yaml +26 -0
  140. moriarty/assets/modules/gaming/playstation.yaml +30 -0
  141. moriarty/assets/modules/gaming/roblox.yaml +31 -0
  142. moriarty/assets/modules/gaming/xbox.yaml +25 -0
  143. moriarty/assets/modules/github.yaml +68 -0
  144. moriarty/assets/modules/gitlab.yaml +60 -0
  145. moriarty/assets/modules/instagram.yaml +48 -0
  146. moriarty/assets/modules/latam/fotolog.yaml +27 -0
  147. moriarty/assets/modules/latam/orkut.yaml +26 -0
  148. moriarty/assets/modules/latam/taringa.yaml +27 -0
  149. moriarty/assets/modules/learning/coursera.yaml +26 -0
  150. moriarty/assets/modules/learning/udemy.yaml +26 -0
  151. moriarty/assets/modules/linkedin.yaml +40 -0
  152. moriarty/assets/modules/marketplaces/depop.yaml +28 -0
  153. moriarty/assets/modules/marketplaces/ebay.yaml +32 -0
  154. moriarty/assets/modules/marketplaces/grailed.yaml +27 -0
  155. moriarty/assets/modules/marketplaces/mercari.yaml +26 -0
  156. moriarty/assets/modules/marketplaces/poshmark.yaml +27 -0
  157. moriarty/assets/modules/marketplaces/reverb.yaml +27 -0
  158. moriarty/assets/modules/marketplaces/vinted.yaml +28 -0
  159. moriarty/assets/modules/medium.yaml +44 -0
  160. moriarty/assets/modules/music/audiomack.yaml +26 -0
  161. moriarty/assets/modules/music/bandcamp.yaml +30 -0
  162. moriarty/assets/modules/music/beatport.yaml +28 -0
  163. moriarty/assets/modules/music/deezer.yaml +26 -0
  164. moriarty/assets/modules/music/discogs.yaml +32 -0
  165. moriarty/assets/modules/music/genius.yaml +26 -0
  166. moriarty/assets/modules/music/lastfm.yaml +30 -0
  167. moriarty/assets/modules/music/mixcloud.yaml +26 -0
  168. moriarty/assets/modules/music/reverbnation.yaml +31 -0
  169. moriarty/assets/modules/music/soundcloud.yaml +31 -0
  170. moriarty/assets/modules/music/spotify.yaml +26 -0
  171. moriarty/assets/modules/music/tidal.yaml +26 -0
  172. moriarty/assets/modules/nsfw/adultwork.yaml +27 -0
  173. moriarty/assets/modules/nsfw/bongacams.yaml +28 -0
  174. moriarty/assets/modules/nsfw/cam4.yaml +28 -0
  175. moriarty/assets/modules/nsfw/chaturbate.yaml +28 -0
  176. moriarty/assets/modules/nsfw/clips4sale.yaml +27 -0
  177. moriarty/assets/modules/nsfw/extralunchmoney.yaml +27 -0
  178. moriarty/assets/modules/nsfw/fansly.yaml +28 -0
  179. moriarty/assets/modules/nsfw/fetlife.yaml +28 -0
  180. moriarty/assets/modules/nsfw/iwantclips.yaml +27 -0
  181. moriarty/assets/modules/nsfw/justforfans.yaml +28 -0
  182. moriarty/assets/modules/nsfw/loyalfans.yaml +28 -0
  183. moriarty/assets/modules/nsfw/manyvids.yaml +27 -0
  184. moriarty/assets/modules/nsfw/myfreecams.yaml +28 -0
  185. moriarty/assets/modules/nsfw/niteflirt.yaml +26 -0
  186. moriarty/assets/modules/nsfw/pornhub.yaml +32 -0
  187. moriarty/assets/modules/nsfw/redtube.yaml +27 -0
  188. moriarty/assets/modules/nsfw/stripchat.yaml +28 -0
  189. moriarty/assets/modules/nsfw/xhamster.yaml +27 -0
  190. moriarty/assets/modules/nsfw/xvideos.yaml +27 -0
  191. moriarty/assets/modules/nsfw/youporn.yaml +27 -0
  192. moriarty/assets/modules/photography/eyeem.yaml +25 -0
  193. moriarty/assets/modules/photography/fotki.yaml +25 -0
  194. moriarty/assets/modules/photography/photobucket.yaml +26 -0
  195. moriarty/assets/modules/photography/smugmug.yaml +25 -0
  196. moriarty/assets/modules/photography/vsco.yaml +27 -0
  197. moriarty/assets/modules/pinterest.yaml +40 -0
  198. moriarty/assets/modules/podcasts/anchor.yaml +26 -0
  199. moriarty/assets/modules/podcasts/castbox.yaml +26 -0
  200. moriarty/assets/modules/podcasts/podbean.yaml +26 -0
  201. moriarty/assets/modules/professional/about.yaml +31 -0
  202. moriarty/assets/modules/professional/academia.yaml +27 -0
  203. moriarty/assets/modules/professional/angellist.yaml +27 -0
  204. moriarty/assets/modules/professional/calendly.yaml +26 -0
  205. moriarty/assets/modules/professional/issuu.yaml +27 -0
  206. moriarty/assets/modules/professional/mendeley.yaml +27 -0
  207. moriarty/assets/modules/professional/notion.yaml +27 -0
  208. moriarty/assets/modules/professional/orcid.yaml +27 -0
  209. moriarty/assets/modules/professional/producthunt.yaml +31 -0
  210. moriarty/assets/modules/professional/researchgate.yaml +32 -0
  211. moriarty/assets/modules/professional/scribd.yaml +27 -0
  212. moriarty/assets/modules/professional/slideshare.yaml +31 -0
  213. moriarty/assets/modules/professional/trello.yaml +26 -0
  214. moriarty/assets/modules/professional/typeform.yaml +27 -0
  215. moriarty/assets/modules/reddit.yaml +46 -0
  216. moriarty/assets/modules/regional/amino.yaml +27 -0
  217. moriarty/assets/modules/regional/ask-fm.yaml +32 -0
  218. moriarty/assets/modules/regional/babycenter.yaml +26 -0
  219. moriarty/assets/modules/regional/cafemom.yaml +27 -0
  220. moriarty/assets/modules/regional/care2.yaml +27 -0
  221. moriarty/assets/modules/regional/diaspora.yaml +26 -0
  222. moriarty/assets/modules/regional/ello.yaml +27 -0
  223. moriarty/assets/modules/regional/gaia.yaml +27 -0
  224. moriarty/assets/modules/regional/habbo.yaml +27 -0
  225. moriarty/assets/modules/regional/imvu.yaml +27 -0
  226. moriarty/assets/modules/regional/lemmy.yaml +27 -0
  227. moriarty/assets/modules/regional/peertube.yaml +26 -0
  228. moriarty/assets/modules/regional/pixelfed.yaml +27 -0
  229. moriarty/assets/modules/regional/plurk.yaml +26 -0
  230. moriarty/assets/modules/regional/recroom.yaml +27 -0
  231. moriarty/assets/modules/regional/secondlife.yaml +26 -0
  232. moriarty/assets/modules/regional/vine-archive.yaml +27 -0
  233. moriarty/assets/modules/regional/vrchat.yaml +27 -0
  234. moriarty/assets/modules/regional/weheartit.yaml +27 -0
  235. moriarty/assets/modules/social/anilist.yaml +27 -0
  236. moriarty/assets/modules/social/beacons.yaml +26 -0
  237. moriarty/assets/modules/social/blogger.yaml +27 -0
  238. moriarty/assets/modules/social/crunchyroll.yaml +27 -0
  239. moriarty/assets/modules/social/discord.yaml +27 -0
  240. moriarty/assets/modules/social/dreamwidth.yaml +26 -0
  241. moriarty/assets/modules/social/facebook.yaml +34 -0
  242. moriarty/assets/modules/social/goodreads.yaml +32 -0
  243. moriarty/assets/modules/social/imdb.yaml +27 -0
  244. moriarty/assets/modules/social/kitsu.yaml +27 -0
  245. moriarty/assets/modules/social/letterboxd.yaml +32 -0
  246. moriarty/assets/modules/social/linktree.yaml +26 -0
  247. moriarty/assets/modules/social/livejournal.yaml +27 -0
  248. moriarty/assets/modules/social/mastodon.yaml +30 -0
  249. moriarty/assets/modules/social/minds.yaml +25 -0
  250. moriarty/assets/modules/social/myanimelist.yaml +32 -0
  251. moriarty/assets/modules/social/ravelry.yaml +27 -0
  252. moriarty/assets/modules/social/snapchat.yaml +25 -0
  253. moriarty/assets/modules/social/telegram.yaml +35 -0
  254. moriarty/assets/modules/social/tiktok.yaml +35 -0
  255. moriarty/assets/modules/social/trakt.yaml +28 -0
  256. moriarty/assets/modules/social/wattpad.yaml +32 -0
  257. moriarty/assets/modules/social/wordpress-com.yaml +26 -0
  258. moriarty/assets/modules/sports/espn.yaml +26 -0
  259. moriarty/assets/modules/sports/untappd.yaml +32 -0
  260. moriarty/assets/modules/stackoverflow.yaml +47 -0
  261. moriarty/assets/modules/steam.yaml +47 -0
  262. moriarty/assets/modules/streaming/caffeine.yaml +25 -0
  263. moriarty/assets/modules/streaming/dlive.yaml +27 -0
  264. moriarty/assets/modules/streaming/trovo.yaml +25 -0
  265. moriarty/assets/modules/travel/airbnb.yaml +26 -0
  266. moriarty/assets/modules/travel/booking.yaml +26 -0
  267. moriarty/assets/modules/travel/couchsurfing.yaml +27 -0
  268. moriarty/assets/modules/travel/tripadvisor.yaml +32 -0
  269. moriarty/assets/modules/tumblr.yaml +40 -0
  270. moriarty/assets/modules/twitch.yaml +48 -0
  271. moriarty/assets/modules/twitter.yaml +39 -0
  272. moriarty/assets/modules/youtube.yaml +42 -0
  273. moriarty/assets/templates/cves/CVE-2017-5638.yaml +27 -0
  274. moriarty/assets/templates/cves/CVE-2018-7600.yaml +30 -0
  275. moriarty/assets/templates/cves/CVE-2019-11510.yaml +27 -0
  276. moriarty/assets/templates/cves/CVE-2019-19781.yaml +28 -0
  277. moriarty/assets/templates/cves/CVE-2020-14882.yaml +28 -0
  278. moriarty/assets/templates/cves/CVE-2020-14883.yaml +29 -0
  279. moriarty/assets/templates/cves/CVE-2020-3452.yaml +28 -0
  280. moriarty/assets/templates/cves/CVE-2020-5902.yaml +28 -0
  281. moriarty/assets/templates/cves/CVE-2021-21972.yaml +31 -0
  282. moriarty/assets/templates/cves/CVE-2021-21985.yaml +28 -0
  283. moriarty/assets/templates/cves/CVE-2021-26084.yaml +30 -0
  284. moriarty/assets/templates/cves/CVE-2021-41773.yaml +25 -0
  285. moriarty/assets/templates/cves/CVE-2021-42013.yaml +28 -0
  286. moriarty/assets/templates/cves/CVE-2021-44228.yaml +27 -0
  287. moriarty/assets/templates/cves/CVE-2022-0185.yaml +21 -0
  288. moriarty/assets/templates/cves/CVE-2022-1388.yaml +36 -0
  289. moriarty/assets/templates/cves/CVE-2022-22954.yaml +28 -0
  290. moriarty/assets/templates/cves/CVE-2022-22965.yaml +31 -0
  291. moriarty/assets/templates/cves/CVE-2022-26134.yaml +27 -0
  292. moriarty/assets/templates/cves/CVE-2023-22515.yaml +27 -0
  293. moriarty/assets/templates/cves/CVE-2023-22527.yaml +29 -0
  294. moriarty/assets/templates/cves/CVE-2023-23752.yaml +33 -0
  295. moriarty/assets/templates/cves/CVE-2023-27350.yaml +27 -0
  296. moriarty/assets/templates/cves/CVE-2023-2868.yaml +27 -0
  297. moriarty/assets/templates/cves/CVE-2023-34362.yaml +27 -0
  298. moriarty/assets/templates/cves/CVE-2023-3519.yaml +28 -0
  299. moriarty/assets/templates/cves/CVE-2023-4966.yaml +27 -0
  300. moriarty/assets/templates/default-logins/admin-weak.yaml +40 -0
  301. moriarty/assets/templates/default-logins/wordpress-default.yaml +38 -0
  302. moriarty/assets/templates/exposures/aws-credentials.yaml +35 -0
  303. moriarty/assets/templates/exposures/backup-files.yaml +36 -0
  304. moriarty/assets/templates/exposures/database-files.yaml +34 -0
  305. moriarty/assets/templates/exposures/docker-exposed.yaml +31 -0
  306. moriarty/assets/templates/exposures/env-exposed.yaml +41 -0
  307. moriarty/assets/templates/exposures/git-exposed.yaml +41 -0
  308. moriarty/assets/templates/exposures/phpinfo.yaml +36 -0
  309. moriarty/assets/templates/exposures/svn-exposed.yaml +28 -0
  310. moriarty/assets/templates/fuzzing/api-endpoints.yaml +39 -0
  311. moriarty/assets/templates/fuzzing/common-files.yaml +37 -0
  312. moriarty/assets/templates/fuzzing/open-redirect-fuzz.yaml +35 -0
  313. moriarty/assets/templates/fuzzing/xss-search-fuzz.yaml +29 -0
  314. moriarty/assets/templates/git-config.yaml +18 -0
  315. moriarty/assets/templates/misconfigurations/cors-misconfiguration.yaml +30 -0
  316. moriarty/assets/templates/misconfigurations/debug-enabled.yaml +29 -0
  317. moriarty/assets/templates/misconfigurations/directory-listing.yaml +33 -0
  318. moriarty/assets/templates/misconfigurations/jwt-none-algo.yaml +30 -0
  319. moriarty/assets/templates/misconfigurations/ssl-tls-weak.yaml +23 -0
  320. moriarty/assets/templates/vulnerabilities/lfi-basic.yaml +31 -0
  321. moriarty/assets/templates/vulnerabilities/open-redirect.yaml +31 -0
  322. moriarty/assets/templates/vulnerabilities/rce-basic.yaml +34 -0
  323. moriarty/assets/templates/vulnerabilities/sqli-error.yaml +39 -0
  324. moriarty/assets/templates/vulnerabilities/ssrf-basic.yaml +31 -0
  325. moriarty/assets/templates/vulnerabilities/xss-reflected.yaml +38 -0
  326. moriarty/assets/templates/vulnerabilities/xxe-basic.yaml +30 -0
  327. moriarty/assets/wordlists/subdomains-1000.txt +1063 -0
  328. moriarty/cli/__init__.py +3 -0
  329. moriarty/cli/app.py +120 -0
  330. moriarty/cli/async_utils.py +19 -0
  331. moriarty/cli/dns.py +83 -0
  332. moriarty/cli/domain_cmd.py +572 -0
  333. moriarty/cli/email.py +383 -0
  334. moriarty/cli/email_investigate.py +224 -0
  335. moriarty/cli/intelligence.py +329 -0
  336. moriarty/cli/output.py +62 -0
  337. moriarty/cli/rdap.py +94 -0
  338. moriarty/cli/state.py +38 -0
  339. moriarty/cli/tls.py +91 -0
  340. moriarty/cli/user.py +227 -0
  341. moriarty/core/cache_backend.py +223 -0
  342. moriarty/core/config_manager.py +303 -0
  343. moriarty/correlator/__init__.py +0 -0
  344. moriarty/data/__init__.py +81 -0
  345. moriarty/data/ioc/__init__.py +142 -0
  346. moriarty/data/ioc/matcher.py +254 -0
  347. moriarty/data/ioc/types.py +267 -0
  348. moriarty/data/local_intelligence.py +507 -0
  349. moriarty/data/signature_loaders/__init__.py +103 -0
  350. moriarty/data/signature_loaders/base.py +54 -0
  351. moriarty/data/signature_loaders/ioc_feed.py +356 -0
  352. moriarty/data/signature_loaders/wappalyzer.py +112 -0
  353. moriarty/dsl/__init__.py +0 -0
  354. moriarty/dsl/loader.py +99 -0
  355. moriarty/dsl/schema.py +47 -0
  356. moriarty/export/__init__.py +0 -0
  357. moriarty/intelligence/__init__.py +27 -0
  358. moriarty/intelligence/__main__.py +150 -0
  359. moriarty/intelligence/config.py +395 -0
  360. moriarty/intelligence/ioc.py +267 -0
  361. moriarty/intelligence/signatures.py +550 -0
  362. moriarty/intelligence/storage.py +501 -0
  363. moriarty/interop/__init__.py +0 -0
  364. moriarty/logging/__init__.py +0 -0
  365. moriarty/logging/config.py +47 -0
  366. moriarty/models/__init__.py +16 -0
  367. moriarty/models/assertion.py +24 -0
  368. moriarty/models/entity.py +22 -0
  369. moriarty/models/evidence.py +37 -0
  370. moriarty/models/relation.py +24 -0
  371. moriarty/models/types.py +28 -0
  372. moriarty/modules/__init__.py +0 -0
  373. moriarty/modules/avatar_hash.py +184 -0
  374. moriarty/modules/directory_fuzzer.py +322 -0
  375. moriarty/modules/dns_scan.py +40 -0
  376. moriarty/modules/domain_scanner.py +620 -0
  377. moriarty/modules/email_check.py +98 -0
  378. moriarty/modules/email_investigate.py +267 -0
  379. moriarty/modules/email_security.py +274 -0
  380. moriarty/modules/googlemaps_lookup.py +106 -0
  381. moriarty/modules/headless_executor.py +201 -0
  382. moriarty/modules/orchestrator.py +60 -0
  383. moriarty/modules/passive_recon.py +444 -0
  384. moriarty/modules/phone_extractor.py +151 -0
  385. moriarty/modules/pipeline_orchestrator.py +726 -0
  386. moriarty/modules/port_scanner.py +129 -0
  387. moriarty/modules/rdap.py +61 -0
  388. moriarty/modules/rdap_extended.py +188 -0
  389. moriarty/modules/stealth_mode.py +610 -0
  390. moriarty/modules/subdomain_discovery.py +595 -0
  391. moriarty/modules/technology_profiler.py +361 -0
  392. moriarty/modules/template_executor.py +239 -0
  393. moriarty/modules/template_scanner.py +1048 -0
  394. moriarty/modules/tls_scan.py +46 -0
  395. moriarty/modules/tls_validator.py +188 -0
  396. moriarty/modules/vuln_scanner.py +483 -0
  397. moriarty/modules/waf_detector.py +585 -0
  398. moriarty/modules/wayback_discovery.py +234 -0
  399. moriarty/modules/web_crawler.py +163 -0
  400. moriarty/net/__init__.py +0 -0
  401. moriarty/net/dns_cache.py +175 -0
  402. moriarty/net/dns_client.py +188 -0
  403. moriarty/net/rdap_client.py +52 -0
  404. moriarty/net/smtp_client.py +114 -0
  405. moriarty/net/tls_client.py +111 -0
  406. moriarty/parsers/__init__.py +0 -0
  407. moriarty/parsers/html_parser.py +136 -0
  408. moriarty/tests/__init__.py +0 -0
  409. moriarty/tests/test_email_service.py +17 -0
  410. moriarty/tests/test_models.py +46 -0
  411. moriarty/tests/test_orchestrator.py +30 -0
  412. moriarty/tests/test_tls_client.py +18 -0
  413. moriarty_project-0.1.6.dist-info/METADATA +388 -0
  414. moriarty_project-0.1.6.dist-info/RECORD +418 -0
  415. moriarty_project-0.1.6.dist-info/WHEEL +4 -0
  416. moriarty_project-0.1.6.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,234 @@
1
+ """Descoberta de URLs históricas via Wayback Machine."""
2
+ import asyncio
3
+ from dataclasses import dataclass
4
+ from typing import List, Optional, Set
5
+ from urllib.parse import urlparse
6
+
7
+ import httpx
8
+ import structlog
9
+ from rich.progress import Progress
10
+
11
+ logger = structlog.get_logger(__name__)
12
+
13
+
14
+ @dataclass
15
+ class WaybackURL:
16
+ """URL histórica do Wayback Machine."""
17
+ url: str
18
+ timestamp: str
19
+ status_code: int
20
+ is_active: bool = False
21
+
22
+
23
+ class WaybackDiscovery:
24
+ """Descoberta de URLs históricas via Wayback Machine."""
25
+
26
+ def __init__(
27
+ self,
28
+ domain: str,
29
+ validate: bool = True,
30
+ filter_extensions: Optional[List[str]] = None,
31
+ filter_status_codes: Optional[List[int]] = None,
32
+ years_back: int = 5,
33
+ timeout: float = 10.0,
34
+ extract_params: bool = True,
35
+ detect_admin: bool = True,
36
+ ):
37
+ self.domain = domain.lower().strip()
38
+ self.validate = validate
39
+ self.filter_extensions = filter_extensions
40
+ self.filter_status_codes = filter_status_codes or [200]
41
+ self.years_back = years_back
42
+ self.timeout = timeout
43
+ self.extract_params = extract_params
44
+ self.detect_admin = detect_admin
45
+ self.urls: Set[str] = set()
46
+ self.parameters: Set[str] = set()
47
+ self.admin_urls: Set[str] = set()
48
+
49
+ async def discover(self) -> List[WaybackURL]:
50
+ """Descobre URLs históricas."""
51
+ logger.info("wayback.discovery.start", domain=self.domain)
52
+
53
+ # Busca no CDX API
54
+ urls = await self._fetch_cdx()
55
+
56
+ # Filtra por extensão se especificado
57
+ if self.filter_extensions:
58
+ urls = self._filter_by_extension(urls)
59
+
60
+ # Extrai parâmetros interessantes
61
+ if self.extract_params:
62
+ self._extract_parameters(urls)
63
+
64
+ # Detecta páginas admin/login
65
+ if self.detect_admin:
66
+ self._detect_admin_pages(urls)
67
+
68
+ # Valida URLs ativas
69
+ if self.validate:
70
+ urls = await self._validate_urls(urls)
71
+
72
+ logger.info("wayback.discovery.complete",
73
+ count=len(urls),
74
+ params=len(self.parameters),
75
+ admin_pages=len(self.admin_urls))
76
+ return urls
77
+
78
+ async def _fetch_cdx(self) -> List[WaybackURL]:
79
+ """Busca URLs no CDX Server."""
80
+ wayback_urls = []
81
+
82
+ try:
83
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
84
+ # CDX API endpoint
85
+ url = f"http://web.archive.org/cdx/search/cdx"
86
+
87
+ # Monta filtro de status codes
88
+ status_filter = '|'.join(map(str, self.filter_status_codes))
89
+
90
+ params = {
91
+ "url": f"{self.domain}/*",
92
+ "output": "json",
93
+ "collapse": "urlkey",
94
+ "fl": "timestamp,original,statuscode",
95
+ "filter": f"statuscode:{status_filter}",
96
+ "from": f"{2024 - self.years_back}",
97
+ }
98
+
99
+ response = await client.get(url, params=params)
100
+
101
+ if response.status_code == 200:
102
+ data = response.json()
103
+
104
+ # Primeira linha é header
105
+ for entry in data[1:]:
106
+ timestamp, original_url, status_code = entry
107
+
108
+ wayback_url = WaybackURL(
109
+ url=original_url,
110
+ timestamp=timestamp,
111
+ status_code=int(status_code),
112
+ )
113
+ wayback_urls.append(wayback_url)
114
+ self.urls.add(original_url)
115
+
116
+ logger.info("wayback.fetch.success", count=len(wayback_urls))
117
+
118
+ except Exception as e:
119
+ logger.error("wayback.fetch.error", error=str(e))
120
+
121
+ return wayback_urls
122
+
123
+ def _filter_by_extension(self, urls: List[WaybackURL]) -> List[WaybackURL]:
124
+ """Filtra URLs por extensão."""
125
+ filtered = []
126
+
127
+ for wayback_url in urls:
128
+ parsed = urlparse(wayback_url.url)
129
+ path = parsed.path.lower()
130
+
131
+ # Verifica se tem alguma extensão desejada
132
+ has_extension = any(path.endswith(f".{ext}") for ext in self.filter_extensions)
133
+
134
+ if has_extension:
135
+ filtered.append(wayback_url)
136
+
137
+ logger.info("wayback.filter.complete", before=len(urls), after=len(filtered))
138
+ return filtered
139
+
140
+ async def _validate_urls(self, urls: List[WaybackURL]) -> List[WaybackURL]:
141
+ """Valida se URLs ainda estão ativas."""
142
+ logger.info("wayback.validation.start", count=len(urls))
143
+
144
+ validated = []
145
+
146
+ async with httpx.AsyncClient(timeout=5.0, follow_redirects=True) as client:
147
+ tasks = [self._check_url(client, wayback_url) for wayback_url in urls]
148
+
149
+ with Progress() as progress:
150
+ task_id = progress.add_task("[cyan]Validando URLs...", total=len(tasks))
151
+
152
+ for coro in asyncio.as_completed(tasks):
153
+ result = await coro
154
+ if result:
155
+ validated.append(result)
156
+ progress.advance(task_id)
157
+
158
+ logger.info("wayback.validation.complete", active=len(validated), total=len(urls))
159
+ return validated
160
+
161
+ async def _check_url(self, client: httpx.AsyncClient, wayback_url: WaybackURL) -> Optional[WaybackURL]:
162
+ """Verifica se URL está ativa."""
163
+ try:
164
+ response = await client.head(wayback_url.url)
165
+
166
+ if response.status_code < 400:
167
+ wayback_url.is_active = True
168
+ return wayback_url
169
+ except:
170
+ pass
171
+
172
+ return None
173
+
174
+ def _extract_parameters(self, urls: List[WaybackURL]):
175
+ """Extrai parâmetros interessantes das URLs."""
176
+ from urllib.parse import urlparse, parse_qs
177
+
178
+ for wb_url in urls:
179
+ parsed = urlparse(wb_url.url)
180
+ if parsed.query:
181
+ params = parse_qs(parsed.query)
182
+ self.parameters.update(params.keys())
183
+
184
+ def _detect_admin_pages(self, urls: List[WaybackURL]):
185
+ """Detecta páginas admin/login."""
186
+ admin_patterns = [
187
+ 'admin', 'login', 'signin', 'signup', 'register',
188
+ 'dashboard', 'panel', 'console', 'manager', 'backend',
189
+ 'wp-admin', 'administrator', 'auth', 'user/login',
190
+ 'account', 'portal', 'control', 'secure', 'private'
191
+ ]
192
+
193
+ for wb_url in urls:
194
+ url_lower = wb_url.url.lower()
195
+ for pattern in admin_patterns:
196
+ if pattern in url_lower:
197
+ self.admin_urls.add(wb_url.url)
198
+ break
199
+
200
+ def export(self, urls: List[WaybackURL], output: str):
201
+ """Exporta URLs para arquivo."""
202
+ with open(output, 'w') as f:
203
+ for wayback_url in sorted(urls, key=lambda x: x.timestamp):
204
+ status = "ACTIVE" if wayback_url.is_active else "ARCHIVED"
205
+ f.write(f"{wayback_url.timestamp} [{status}] {wayback_url.url}\n")
206
+
207
+ logger.info("wayback.export.complete", file=output, count=len(urls))
208
+
209
+ def export_url_list(self, output: str):
210
+ """Exporta lista simples de URLs para outras ferramentas."""
211
+ with open(output, 'w') as f:
212
+ for url in sorted(self.urls):
213
+ f.write(f"{url}\n")
214
+
215
+ logger.info("wayback.export.urllist", file=output, count=len(self.urls))
216
+
217
+ def export_parameters(self, output: str):
218
+ """Exporta lista de parâmetros encontrados."""
219
+ with open(output, 'w') as f:
220
+ for param in sorted(self.parameters):
221
+ f.write(f"{param}\n")
222
+
223
+ logger.info("wayback.export.params", file=output, count=len(self.parameters))
224
+
225
+ def export_admin_urls(self, output: str):
226
+ """Exporta URLs de admin/login."""
227
+ with open(output, 'w') as f:
228
+ for url in sorted(self.admin_urls):
229
+ f.write(f"{url}\n")
230
+
231
+ logger.info("wayback.export.admin", file=output, count=len(self.admin_urls))
232
+
233
+
234
+ __all__ = ["WaybackDiscovery", "WaybackURL"]
@@ -0,0 +1,163 @@
1
+ """Crawler HTTP leve focado em enumeração de rotas e formulários."""
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import random
6
+ from dataclasses import dataclass, field
7
+ from typing import Dict, List, Optional, Set, TYPE_CHECKING
8
+
9
+ import httpx
10
+ from selectolax.parser import HTMLParser
11
+ import structlog
12
+
13
+ if TYPE_CHECKING: # pragma: no cover - apenas para type hints
14
+ from moriarty.modules.stealth_mode import StealthMode
15
+
16
+ logger = structlog.get_logger(__name__)
17
+
18
+
19
+ @dataclass
20
+ class CrawlPage:
21
+ url: str
22
+ status: int
23
+ title: Optional[str] = None
24
+ forms: List[Dict[str, str]] = field(default_factory=list)
25
+ links: List[str] = field(default_factory=list)
26
+
27
+
28
+ class WebCrawler:
29
+ """Crawler simples limitado a um domínio, ideal para pré-enumeração."""
30
+
31
+ def __init__(
32
+ self,
33
+ base_url: str,
34
+ max_pages: int = 100,
35
+ max_depth: int = 2,
36
+ concurrency: int = 10,
37
+ follow_subdomains: bool = False,
38
+ user_agent: str = "Mozilla/5.0 (Moriarty Recon)",
39
+ stealth: Optional["StealthMode"] = None,
40
+ ):
41
+ self.base_url = base_url.rstrip("/")
42
+ self.max_pages = max_pages
43
+ self.max_depth = max_depth
44
+ self.concurrency = concurrency
45
+ self.follow_subdomains = follow_subdomains
46
+ self.visited: Set[str] = set()
47
+ self.results: Dict[str, CrawlPage] = {}
48
+ self.stealth = stealth
49
+ self.user_agent = user_agent
50
+
51
+ effective_concurrency = concurrency
52
+ if self.stealth and getattr(self.stealth.config, "timing_randomization", False):
53
+ # Reduz concorrência para modos stealth altos
54
+ effective_concurrency = max(2, min(concurrency, int(concurrency / (self.stealth.level or 1))))
55
+
56
+ self.sem = asyncio.Semaphore(effective_concurrency)
57
+ self.session = httpx.AsyncClient(timeout=10.0, follow_redirects=True)
58
+
59
+ parsed = httpx.URL(self.base_url)
60
+ self._host = parsed.host
61
+ self._scheme = parsed.scheme
62
+
63
+ async def close(self) -> None:
64
+ await self.session.aclose()
65
+
66
+ async def crawl(self) -> Dict[str, CrawlPage]:
67
+ queue: asyncio.Queue = asyncio.Queue()
68
+ await queue.put((self.base_url, 0))
69
+
70
+ async def worker():
71
+ while True:
72
+ try:
73
+ url, depth = queue.get_nowait()
74
+ except asyncio.QueueEmpty:
75
+ break
76
+ if len(self.results) >= self.max_pages or depth > self.max_depth:
77
+ continue
78
+ if url in self.visited:
79
+ continue
80
+ self.visited.add(url)
81
+ await self._fetch(url, depth, queue)
82
+
83
+ workers = [asyncio.create_task(worker()) for _ in range(self.concurrency)]
84
+ await asyncio.gather(*workers)
85
+ return self.results
86
+
87
+ async def _fetch(self, url: str, depth: int, queue: asyncio.Queue) -> None:
88
+ async with self.sem:
89
+ try:
90
+ await self._stealth_delay()
91
+ response = await self.session.get(url, headers=self._build_headers())
92
+ except Exception as exc:
93
+ logger.debug("crawler.fetch.error", url=url, error=str(exc))
94
+ return
95
+
96
+ page = CrawlPage(url=url, status=response.status_code)
97
+ if response.status_code >= 400 or not response.headers.get("content-type", "").startswith("text"):
98
+ self.results[url] = page
99
+ return
100
+
101
+ parser = HTMLParser(response.text)
102
+ title = parser.css_first("title")
103
+ page.title = title.text(strip=True) if title else None
104
+
105
+ # Forms
106
+ for form in parser.css("form"):
107
+ action = form.attributes.get("action", url)
108
+ method = form.attributes.get("method", "GET").upper()
109
+ inputs = [inp.attributes.get("name") for inp in form.css("input") if inp.attributes.get("name")]
110
+ page.forms.append(
111
+ {
112
+ "action": action,
113
+ "method": method,
114
+ "inputs": ",".join(inputs),
115
+ }
116
+ )
117
+
118
+ # Links
119
+ links: Set[str] = set()
120
+ for anchor in parser.css("a"):
121
+ href = anchor.attributes.get("href")
122
+ if not href:
123
+ continue
124
+ href = href.strip()
125
+ if href.startswith("javascript:") or href.startswith("mailto:"):
126
+ continue
127
+ absolute = httpx.URL(href, base=httpx.URL(url)).human_repr()
128
+ if not self._should_follow(absolute):
129
+ continue
130
+ links.add(absolute)
131
+ if absolute not in self.visited and len(self.results) < self.max_pages:
132
+ await queue.put((absolute, depth + 1))
133
+ page.links = sorted(links)
134
+ self.results[url] = page
135
+
136
+ def _should_follow(self, url: str) -> bool:
137
+ parsed = httpx.URL(url)
138
+ if parsed.scheme not in {"http", "https"}:
139
+ return False
140
+ if not self.follow_subdomains and parsed.host != self._host:
141
+ return False
142
+ if not parsed.host.endswith(self._host):
143
+ return False
144
+ return True
145
+
146
+ def _build_headers(self) -> Dict[str, str]:
147
+ headers: Dict[str, str] = {"User-Agent": self.user_agent, "Accept": "*/*"}
148
+ if self.stealth:
149
+ stealth_headers = self.stealth.get_random_headers()
150
+ headers.update(stealth_headers)
151
+ headers.setdefault("User-Agent", stealth_headers.get("User-Agent", self.user_agent))
152
+ return headers
153
+
154
+ async def _stealth_delay(self) -> None:
155
+ if not self.stealth:
156
+ return
157
+ config = getattr(self.stealth, "config", None)
158
+ if not config or not getattr(config, "timing_randomization", False):
159
+ return
160
+ await asyncio.sleep(random.uniform(0.05, 0.2) * max(1, self.stealth.level))
161
+
162
+
163
+ __all__ = ["WebCrawler", "CrawlPage"]
File without changes
@@ -0,0 +1,175 @@
1
+ """Cache DNS com suporte a TTL, persistência e métricas."""
2
+ import asyncio
3
+ import json
4
+ import time
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional, Tuple
7
+
8
+ import structlog
9
+
10
+ from moriarty.core.cache_backend import (
11
+ BaseCacheBackend,
12
+ CacheRecord,
13
+ MemoryCacheBackend,
14
+ SQLiteCacheBackend,
15
+ )
16
+
17
+ logger = structlog.get_logger(__name__)
18
+
19
+
20
+ class DNSCache:
21
+ """Cache DNS thread-safe com TTL."""
22
+
23
+ def __init__(self):
24
+ self._lock = asyncio.Lock()
25
+ self.config_manager = self._load_config_manager()
26
+ self.cache_config = getattr(self.config_manager, "cache", None) if self.config_manager else None
27
+ self.backend: BaseCacheBackend = self._initialize_backend()
28
+ self.metrics: Dict[str, Any] = {
29
+ "hits": 0,
30
+ "misses": 0,
31
+ "backend": self.backend.name,
32
+ "warmed": 0,
33
+ }
34
+
35
+ if self.cache_config and getattr(self.cache_config, "warmup_enabled", False):
36
+ self._warm_cache()
37
+
38
+ async def get(self, domain: str, record_type: str) -> Optional[List[Any]]:
39
+ """Busca no cache."""
40
+ key = (domain.lower(), record_type.upper())
41
+
42
+ async with self._lock:
43
+ record = self.backend.get(key)
44
+ if record is None:
45
+ logger.debug("dns.cache.miss", domain=domain, type=record_type)
46
+ self.metrics["misses"] += 1
47
+ return None
48
+
49
+ ttl_remaining = int(record.expires_at - time.time())
50
+ if ttl_remaining <= 0:
51
+ logger.debug("dns.cache.expired", domain=domain, type=record_type)
52
+ self.backend.delete(key)
53
+ self.metrics["misses"] += 1
54
+ return None
55
+
56
+ logger.debug(
57
+ "dns.cache.hit",
58
+ domain=domain,
59
+ type=record_type,
60
+ ttl_remaining=ttl_remaining,
61
+ )
62
+ self.metrics["hits"] += 1
63
+ return record.value
64
+
65
+ async def set(self, domain: str, record_type: str, value: List[Any], ttl: int = 300):
66
+ """Armazena no cache."""
67
+ key = (domain.lower(), record_type.upper())
68
+
69
+ async with self._lock:
70
+ self.backend.set(key, value, ttl)
71
+ logger.debug("dns.cache.set", domain=domain, type=record_type, ttl=ttl)
72
+
73
+ async def clear(self):
74
+ """Limpa o cache."""
75
+ async with self._lock:
76
+ self.backend.clear()
77
+ logger.info("dns.cache.cleared")
78
+
79
+ async def purge_expired(self):
80
+ """Remove entradas expiradas."""
81
+ async with self._lock:
82
+ self.backend.purge_expired()
83
+ logger.info("dns.cache.purged")
84
+
85
+ def stats(self) -> Dict[str, Any]:
86
+ """Retorna estatísticas do cache."""
87
+ base_stats = dict(self.metrics)
88
+ base_stats.update(self.backend.stats())
89
+ return base_stats
90
+
91
+ def _load_config_manager(self):
92
+ try:
93
+ from moriarty.core.config_manager import config_manager
94
+
95
+ return config_manager
96
+ except Exception:
97
+ return None
98
+
99
+ def _initialize_backend(self) -> BaseCacheBackend:
100
+ backend_name = "memory"
101
+ max_size = 10000
102
+ eviction_policy = "lru"
103
+ sqlite_path = None
104
+
105
+ if self.cache_config:
106
+ backend_name = getattr(self.cache_config, "backend", "memory").lower()
107
+ max_size = getattr(self.cache_config, "max_size", 10000) or 10000
108
+ eviction_policy = getattr(self.cache_config, "eviction_policy", "lru")
109
+ sqlite_path = getattr(self.cache_config, "sqlite_path", None)
110
+
111
+ if backend_name == "sqlite":
112
+ db_path = Path(sqlite_path) if sqlite_path else self._default_sqlite_path()
113
+ db_path.parent.mkdir(parents=True, exist_ok=True)
114
+ logger.debug("dns.cache.backend", backend="sqlite", path=str(db_path))
115
+ return SQLiteCacheBackend(db_path, max_size)
116
+
117
+ logger.debug("dns.cache.backend", backend="memory", policy=eviction_policy)
118
+ return MemoryCacheBackend(max_size=max_size, eviction_policy=eviction_policy)
119
+
120
+ def _default_sqlite_path(self) -> Path:
121
+ if self.config_manager:
122
+ return self.config_manager.config_dir / "cache" / "dns_cache.db"
123
+ return Path.home() / ".moriarty" / "cache" / "dns_cache.db"
124
+
125
+ def _warm_cache(self) -> None:
126
+ if not self.config_manager:
127
+ return
128
+
129
+ warmup_dir = self.config_manager.config_dir / "warmup"
130
+ warmup_file = warmup_dir / "dns.json"
131
+
132
+ if not warmup_file.exists():
133
+ return
134
+
135
+ try:
136
+ with open(warmup_file, "r", encoding="utf-8") as handle:
137
+ payload = json.load(handle)
138
+
139
+ records = []
140
+ for item in payload.get("records", []):
141
+ domain = item.get("domain")
142
+ record_type = item.get("type", "A")
143
+ values = item.get("values", [])
144
+ ttl = int(item.get("ttl", 300))
145
+ if not domain or not values:
146
+ continue
147
+ expires_at = time.time() + ttl
148
+ record = CacheRecord(
149
+ key=(domain.lower(), record_type.upper()),
150
+ value=values,
151
+ ttl=ttl,
152
+ expires_at=expires_at,
153
+ last_access=time.time(),
154
+ )
155
+ records.append(record)
156
+
157
+ if records:
158
+ self.backend.warm(records)
159
+ self.metrics["warmed"] = len(records)
160
+ logger.info("dns.cache.warmed", records=len(records))
161
+
162
+ except Exception as exc: # pragma: no cover - leitura auxiliar
163
+ logger.warning("dns.cache.warm_failed", error=str(exc))
164
+
165
+
166
+ # Singleton global
167
+ _global_cache: Optional[DNSCache] = None
168
+
169
+
170
+ def get_global_cache() -> DNSCache:
171
+ """Retorna cache global."""
172
+ global _global_cache
173
+ if _global_cache is None:
174
+ _global_cache = DNSCache()
175
+ return _global_cache