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.
- moriarty/__init__.py +5 -0
- moriarty/adapters/__init__.py +0 -0
- moriarty/agent/__init__.py +0 -0
- moriarty/assets/modules/.gitkeep +0 -0
- moriarty/assets/modules/asia/douban.yaml +19 -0
- moriarty/assets/modules/asia/kakao.yaml +19 -0
- moriarty/assets/modules/asia/line.yaml +19 -0
- moriarty/assets/modules/asia/mixi.yaml +19 -0
- moriarty/assets/modules/asia/naver.yaml +19 -0
- moriarty/assets/modules/asia/qq.yaml +19 -0
- moriarty/assets/modules/asia/vk.yaml +19 -0
- moriarty/assets/modules/asia/wechat.yaml +19 -0
- moriarty/assets/modules/asia/weibo.yaml +19 -0
- moriarty/assets/modules/asia/xiaohongshu.yaml +19 -0
- moriarty/assets/modules/behance.yaml +47 -0
- moriarty/assets/modules/business/crunchbase.yaml +27 -0
- moriarty/assets/modules/business/fiverr.yaml +32 -0
- moriarty/assets/modules/business/freelancer.yaml +27 -0
- moriarty/assets/modules/business/glassdoor.yaml +27 -0
- moriarty/assets/modules/business/guru.yaml +26 -0
- moriarty/assets/modules/business/indeed.yaml +25 -0
- moriarty/assets/modules/business/monster.yaml +25 -0
- moriarty/assets/modules/business/peopleperhour.yaml +26 -0
- moriarty/assets/modules/business/toptal.yaml +28 -0
- moriarty/assets/modules/business/upwork.yaml +27 -0
- moriarty/assets/modules/business/ziprecruiter.yaml +25 -0
- moriarty/assets/modules/content/buymeacoffee.yaml +27 -0
- moriarty/assets/modules/content/gumroad.yaml +27 -0
- moriarty/assets/modules/content/ko-fi.yaml +32 -0
- moriarty/assets/modules/content/onlyfans.yaml +27 -0
- moriarty/assets/modules/content/patreon.yaml +33 -0
- moriarty/assets/modules/content/substack.yaml +32 -0
- moriarty/assets/modules/creative/500px.yaml +31 -0
- moriarty/assets/modules/creative/artstation.yaml +33 -0
- moriarty/assets/modules/creative/deviantart.yaml +32 -0
- moriarty/assets/modules/creative/flickr.yaml +31 -0
- moriarty/assets/modules/creative/pexels.yaml +26 -0
- moriarty/assets/modules/creative/unsplash.yaml +26 -0
- moriarty/assets/modules/creative/vimeo.yaml +31 -0
- moriarty/assets/modules/crypto/binance.yaml +27 -0
- moriarty/assets/modules/crypto/bitcointalk.yaml +33 -0
- moriarty/assets/modules/crypto/coinbase.yaml +26 -0
- moriarty/assets/modules/crypto/etherscan.yaml +32 -0
- moriarty/assets/modules/crypto/foundation.yaml +28 -0
- moriarty/assets/modules/crypto/kraken.yaml +27 -0
- moriarty/assets/modules/crypto/mirror.yaml +27 -0
- moriarty/assets/modules/crypto/niftygateway.yaml +26 -0
- moriarty/assets/modules/crypto/opensea.yaml +32 -0
- moriarty/assets/modules/crypto/rarible.yaml +27 -0
- moriarty/assets/modules/crypto/superrare.yaml +29 -0
- moriarty/assets/modules/dating/bumble.yaml +25 -0
- moriarty/assets/modules/dating/grindr.yaml +27 -0
- moriarty/assets/modules/dating/happn.yaml +25 -0
- moriarty/assets/modules/dating/her.yaml +27 -0
- moriarty/assets/modules/dating/hinge.yaml +25 -0
- moriarty/assets/modules/dating/match.yaml +25 -0
- moriarty/assets/modules/dating/meetme.yaml +27 -0
- moriarty/assets/modules/dating/okcupid.yaml +25 -0
- moriarty/assets/modules/dating/pof.yaml +25 -0
- moriarty/assets/modules/dating/tinder.yaml +25 -0
- moriarty/assets/modules/dating-nsfw/adultfriendfinder.yaml +28 -0
- moriarty/assets/modules/dating-nsfw/ashley-madison.yaml +26 -0
- moriarty/assets/modules/design/adobe-portfolio.yaml +27 -0
- moriarty/assets/modules/design/carbonmade.yaml +27 -0
- moriarty/assets/modules/design/cgsociety.yaml +27 -0
- moriarty/assets/modules/design/coroflot.yaml +27 -0
- moriarty/assets/modules/design/figma.yaml +27 -0
- moriarty/assets/modules/design/sketch.yaml +26 -0
- moriarty/assets/modules/dev/bitbucket.yaml +35 -0
- moriarty/assets/modules/dev/codeforces.yaml +32 -0
- moriarty/assets/modules/dev/codepen.yaml +34 -0
- moriarty/assets/modules/dev/hackerone.yaml +32 -0
- moriarty/assets/modules/dev/hackthebox.yaml +27 -0
- moriarty/assets/modules/dev/huggingface.yaml +27 -0
- moriarty/assets/modules/dev/kaggle.yaml +32 -0
- moriarty/assets/modules/dev/leetcode.yaml +32 -0
- moriarty/assets/modules/dev/replit.yaml +31 -0
- moriarty/assets/modules/dribbble.yaml +53 -0
- moriarty/assets/modules/ecommerce/etsy.yaml +32 -0
- moriarty/assets/modules/education/duolingo.yaml +32 -0
- moriarty/assets/modules/education/edx.yaml +26 -0
- moriarty/assets/modules/education/khanacademy.yaml +26 -0
- moriarty/assets/modules/education/lynda.yaml +27 -0
- moriarty/assets/modules/education/memrise.yaml +27 -0
- moriarty/assets/modules/education/pluralsight.yaml +27 -0
- moriarty/assets/modules/education/skillshare.yaml +27 -0
- moriarty/assets/modules/education/udacity.yaml +27 -0
- moriarty/assets/modules/email/github_email.yaml +40 -0
- moriarty/assets/modules/email/gravatar.yaml +23 -0
- moriarty/assets/modules/europe/badoo.yaml +19 -0
- moriarty/assets/modules/europe/lovoo.yaml +19 -0
- moriarty/assets/modules/europe/myspace.yaml +19 -0
- moriarty/assets/modules/europe/netlog.yaml +19 -0
- moriarty/assets/modules/europe/ok.yaml +19 -0
- moriarty/assets/modules/europe/skyrock.yaml +19 -0
- moriarty/assets/modules/europe/studivz.yaml +19 -0
- moriarty/assets/modules/europe/tuenti.yaml +19 -0
- moriarty/assets/modules/europe/viadeo.yaml +19 -0
- moriarty/assets/modules/europe/xing.yaml +19 -0
- moriarty/assets/modules/fitness/fitbit.yaml +27 -0
- moriarty/assets/modules/fitness/garmin.yaml +27 -0
- moriarty/assets/modules/fitness/myfitnesspal.yaml +27 -0
- moriarty/assets/modules/fitness/strava.yaml +33 -0
- moriarty/assets/modules/fitness/zwift.yaml +28 -0
- moriarty/assets/modules/food/allrecipes.yaml +27 -0
- moriarty/assets/modules/food/tasty.yaml +27 -0
- moriarty/assets/modules/food/yelp.yaml +32 -0
- moriarty/assets/modules/food/zomato.yaml +28 -0
- moriarty/assets/modules/forums/4chan.yaml +26 -0
- moriarty/assets/modules/forums/8kun.yaml +26 -0
- moriarty/assets/modules/forums/9gag.yaml +26 -0
- moriarty/assets/modules/forums/discourse.yaml +26 -0
- moriarty/assets/modules/forums/disqus.yaml +31 -0
- moriarty/assets/modules/forums/hackernews.yaml +32 -0
- moriarty/assets/modules/forums/launchpad.yaml +27 -0
- moriarty/assets/modules/forums/phpbb.yaml +25 -0
- moriarty/assets/modules/forums/quora.yaml +32 -0
- moriarty/assets/modules/forums/serverfault.yaml +27 -0
- moriarty/assets/modules/forums/slashdot.yaml +28 -0
- moriarty/assets/modules/forums/stackexchange.yaml +32 -0
- moriarty/assets/modules/forums/superuser.yaml +27 -0
- moriarty/assets/modules/forums/vbulletin.yaml +25 -0
- moriarty/assets/modules/forums/xenforo.yaml +25 -0
- moriarty/assets/modules/forums-nsfw/kiwifarms.yaml +25 -0
- moriarty/assets/modules/forums-nsfw/lolcow.yaml +26 -0
- moriarty/assets/modules/gaming/apextracker.yaml +27 -0
- moriarty/assets/modules/gaming/battlenet.yaml +26 -0
- moriarty/assets/modules/gaming/chess.yaml +30 -0
- moriarty/assets/modules/gaming/discord-public.yaml +27 -0
- moriarty/assets/modules/gaming/dotabuff.yaml +32 -0
- moriarty/assets/modules/gaming/epicgames.yaml +25 -0
- moriarty/assets/modules/gaming/faceit.yaml +33 -0
- moriarty/assets/modules/gaming/fortnitetracker.yaml +32 -0
- moriarty/assets/modules/gaming/gog.yaml +26 -0
- moriarty/assets/modules/gaming/itch.yaml +32 -0
- moriarty/assets/modules/gaming/kongregate.yaml +25 -0
- moriarty/assets/modules/gaming/minecraft.yaml +31 -0
- moriarty/assets/modules/gaming/opgg.yaml +32 -0
- moriarty/assets/modules/gaming/origin.yaml +26 -0
- moriarty/assets/modules/gaming/playstation.yaml +30 -0
- moriarty/assets/modules/gaming/roblox.yaml +31 -0
- moriarty/assets/modules/gaming/xbox.yaml +25 -0
- moriarty/assets/modules/github.yaml +68 -0
- moriarty/assets/modules/gitlab.yaml +60 -0
- moriarty/assets/modules/instagram.yaml +48 -0
- moriarty/assets/modules/latam/fotolog.yaml +27 -0
- moriarty/assets/modules/latam/orkut.yaml +26 -0
- moriarty/assets/modules/latam/taringa.yaml +27 -0
- moriarty/assets/modules/learning/coursera.yaml +26 -0
- moriarty/assets/modules/learning/udemy.yaml +26 -0
- moriarty/assets/modules/linkedin.yaml +40 -0
- moriarty/assets/modules/marketplaces/depop.yaml +28 -0
- moriarty/assets/modules/marketplaces/ebay.yaml +32 -0
- moriarty/assets/modules/marketplaces/grailed.yaml +27 -0
- moriarty/assets/modules/marketplaces/mercari.yaml +26 -0
- moriarty/assets/modules/marketplaces/poshmark.yaml +27 -0
- moriarty/assets/modules/marketplaces/reverb.yaml +27 -0
- moriarty/assets/modules/marketplaces/vinted.yaml +28 -0
- moriarty/assets/modules/medium.yaml +44 -0
- moriarty/assets/modules/music/audiomack.yaml +26 -0
- moriarty/assets/modules/music/bandcamp.yaml +30 -0
- moriarty/assets/modules/music/beatport.yaml +28 -0
- moriarty/assets/modules/music/deezer.yaml +26 -0
- moriarty/assets/modules/music/discogs.yaml +32 -0
- moriarty/assets/modules/music/genius.yaml +26 -0
- moriarty/assets/modules/music/lastfm.yaml +30 -0
- moriarty/assets/modules/music/mixcloud.yaml +26 -0
- moriarty/assets/modules/music/reverbnation.yaml +31 -0
- moriarty/assets/modules/music/soundcloud.yaml +31 -0
- moriarty/assets/modules/music/spotify.yaml +26 -0
- moriarty/assets/modules/music/tidal.yaml +26 -0
- moriarty/assets/modules/nsfw/adultwork.yaml +27 -0
- moriarty/assets/modules/nsfw/bongacams.yaml +28 -0
- moriarty/assets/modules/nsfw/cam4.yaml +28 -0
- moriarty/assets/modules/nsfw/chaturbate.yaml +28 -0
- moriarty/assets/modules/nsfw/clips4sale.yaml +27 -0
- moriarty/assets/modules/nsfw/extralunchmoney.yaml +27 -0
- moriarty/assets/modules/nsfw/fansly.yaml +28 -0
- moriarty/assets/modules/nsfw/fetlife.yaml +28 -0
- moriarty/assets/modules/nsfw/iwantclips.yaml +27 -0
- moriarty/assets/modules/nsfw/justforfans.yaml +28 -0
- moriarty/assets/modules/nsfw/loyalfans.yaml +28 -0
- moriarty/assets/modules/nsfw/manyvids.yaml +27 -0
- moriarty/assets/modules/nsfw/myfreecams.yaml +28 -0
- moriarty/assets/modules/nsfw/niteflirt.yaml +26 -0
- moriarty/assets/modules/nsfw/pornhub.yaml +32 -0
- moriarty/assets/modules/nsfw/redtube.yaml +27 -0
- moriarty/assets/modules/nsfw/stripchat.yaml +28 -0
- moriarty/assets/modules/nsfw/xhamster.yaml +27 -0
- moriarty/assets/modules/nsfw/xvideos.yaml +27 -0
- moriarty/assets/modules/nsfw/youporn.yaml +27 -0
- moriarty/assets/modules/photography/eyeem.yaml +25 -0
- moriarty/assets/modules/photography/fotki.yaml +25 -0
- moriarty/assets/modules/photography/photobucket.yaml +26 -0
- moriarty/assets/modules/photography/smugmug.yaml +25 -0
- moriarty/assets/modules/photography/vsco.yaml +27 -0
- moriarty/assets/modules/pinterest.yaml +40 -0
- moriarty/assets/modules/podcasts/anchor.yaml +26 -0
- moriarty/assets/modules/podcasts/castbox.yaml +26 -0
- moriarty/assets/modules/podcasts/podbean.yaml +26 -0
- moriarty/assets/modules/professional/about.yaml +31 -0
- moriarty/assets/modules/professional/academia.yaml +27 -0
- moriarty/assets/modules/professional/angellist.yaml +27 -0
- moriarty/assets/modules/professional/calendly.yaml +26 -0
- moriarty/assets/modules/professional/issuu.yaml +27 -0
- moriarty/assets/modules/professional/mendeley.yaml +27 -0
- moriarty/assets/modules/professional/notion.yaml +27 -0
- moriarty/assets/modules/professional/orcid.yaml +27 -0
- moriarty/assets/modules/professional/producthunt.yaml +31 -0
- moriarty/assets/modules/professional/researchgate.yaml +32 -0
- moriarty/assets/modules/professional/scribd.yaml +27 -0
- moriarty/assets/modules/professional/slideshare.yaml +31 -0
- moriarty/assets/modules/professional/trello.yaml +26 -0
- moriarty/assets/modules/professional/typeform.yaml +27 -0
- moriarty/assets/modules/reddit.yaml +46 -0
- moriarty/assets/modules/regional/amino.yaml +27 -0
- moriarty/assets/modules/regional/ask-fm.yaml +32 -0
- moriarty/assets/modules/regional/babycenter.yaml +26 -0
- moriarty/assets/modules/regional/cafemom.yaml +27 -0
- moriarty/assets/modules/regional/care2.yaml +27 -0
- moriarty/assets/modules/regional/diaspora.yaml +26 -0
- moriarty/assets/modules/regional/ello.yaml +27 -0
- moriarty/assets/modules/regional/gaia.yaml +27 -0
- moriarty/assets/modules/regional/habbo.yaml +27 -0
- moriarty/assets/modules/regional/imvu.yaml +27 -0
- moriarty/assets/modules/regional/lemmy.yaml +27 -0
- moriarty/assets/modules/regional/peertube.yaml +26 -0
- moriarty/assets/modules/regional/pixelfed.yaml +27 -0
- moriarty/assets/modules/regional/plurk.yaml +26 -0
- moriarty/assets/modules/regional/recroom.yaml +27 -0
- moriarty/assets/modules/regional/secondlife.yaml +26 -0
- moriarty/assets/modules/regional/vine-archive.yaml +27 -0
- moriarty/assets/modules/regional/vrchat.yaml +27 -0
- moriarty/assets/modules/regional/weheartit.yaml +27 -0
- moriarty/assets/modules/social/anilist.yaml +27 -0
- moriarty/assets/modules/social/beacons.yaml +26 -0
- moriarty/assets/modules/social/blogger.yaml +27 -0
- moriarty/assets/modules/social/crunchyroll.yaml +27 -0
- moriarty/assets/modules/social/discord.yaml +27 -0
- moriarty/assets/modules/social/dreamwidth.yaml +26 -0
- moriarty/assets/modules/social/facebook.yaml +34 -0
- moriarty/assets/modules/social/goodreads.yaml +32 -0
- moriarty/assets/modules/social/imdb.yaml +27 -0
- moriarty/assets/modules/social/kitsu.yaml +27 -0
- moriarty/assets/modules/social/letterboxd.yaml +32 -0
- moriarty/assets/modules/social/linktree.yaml +26 -0
- moriarty/assets/modules/social/livejournal.yaml +27 -0
- moriarty/assets/modules/social/mastodon.yaml +30 -0
- moriarty/assets/modules/social/minds.yaml +25 -0
- moriarty/assets/modules/social/myanimelist.yaml +32 -0
- moriarty/assets/modules/social/ravelry.yaml +27 -0
- moriarty/assets/modules/social/snapchat.yaml +25 -0
- moriarty/assets/modules/social/telegram.yaml +35 -0
- moriarty/assets/modules/social/tiktok.yaml +35 -0
- moriarty/assets/modules/social/trakt.yaml +28 -0
- moriarty/assets/modules/social/wattpad.yaml +32 -0
- moriarty/assets/modules/social/wordpress-com.yaml +26 -0
- moriarty/assets/modules/sports/espn.yaml +26 -0
- moriarty/assets/modules/sports/untappd.yaml +32 -0
- moriarty/assets/modules/stackoverflow.yaml +47 -0
- moriarty/assets/modules/steam.yaml +47 -0
- moriarty/assets/modules/streaming/caffeine.yaml +25 -0
- moriarty/assets/modules/streaming/dlive.yaml +27 -0
- moriarty/assets/modules/streaming/trovo.yaml +25 -0
- moriarty/assets/modules/travel/airbnb.yaml +26 -0
- moriarty/assets/modules/travel/booking.yaml +26 -0
- moriarty/assets/modules/travel/couchsurfing.yaml +27 -0
- moriarty/assets/modules/travel/tripadvisor.yaml +32 -0
- moriarty/assets/modules/tumblr.yaml +40 -0
- moriarty/assets/modules/twitch.yaml +48 -0
- moriarty/assets/modules/twitter.yaml +39 -0
- moriarty/assets/modules/youtube.yaml +42 -0
- moriarty/assets/templates/cves/CVE-2017-5638.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2018-7600.yaml +30 -0
- moriarty/assets/templates/cves/CVE-2019-11510.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2019-19781.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2020-14882.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2020-14883.yaml +29 -0
- moriarty/assets/templates/cves/CVE-2020-3452.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2020-5902.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2021-21972.yaml +31 -0
- moriarty/assets/templates/cves/CVE-2021-21985.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2021-26084.yaml +30 -0
- moriarty/assets/templates/cves/CVE-2021-41773.yaml +25 -0
- moriarty/assets/templates/cves/CVE-2021-42013.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2021-44228.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2022-0185.yaml +21 -0
- moriarty/assets/templates/cves/CVE-2022-1388.yaml +36 -0
- moriarty/assets/templates/cves/CVE-2022-22954.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2022-22965.yaml +31 -0
- moriarty/assets/templates/cves/CVE-2022-26134.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-22515.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-22527.yaml +29 -0
- moriarty/assets/templates/cves/CVE-2023-23752.yaml +33 -0
- moriarty/assets/templates/cves/CVE-2023-27350.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-2868.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-34362.yaml +27 -0
- moriarty/assets/templates/cves/CVE-2023-3519.yaml +28 -0
- moriarty/assets/templates/cves/CVE-2023-4966.yaml +27 -0
- moriarty/assets/templates/default-logins/admin-weak.yaml +40 -0
- moriarty/assets/templates/default-logins/wordpress-default.yaml +38 -0
- moriarty/assets/templates/exposures/aws-credentials.yaml +35 -0
- moriarty/assets/templates/exposures/backup-files.yaml +36 -0
- moriarty/assets/templates/exposures/database-files.yaml +34 -0
- moriarty/assets/templates/exposures/docker-exposed.yaml +31 -0
- moriarty/assets/templates/exposures/env-exposed.yaml +41 -0
- moriarty/assets/templates/exposures/git-exposed.yaml +41 -0
- moriarty/assets/templates/exposures/phpinfo.yaml +36 -0
- moriarty/assets/templates/exposures/svn-exposed.yaml +28 -0
- moriarty/assets/templates/fuzzing/api-endpoints.yaml +39 -0
- moriarty/assets/templates/fuzzing/common-files.yaml +37 -0
- moriarty/assets/templates/fuzzing/open-redirect-fuzz.yaml +35 -0
- moriarty/assets/templates/fuzzing/xss-search-fuzz.yaml +29 -0
- moriarty/assets/templates/git-config.yaml +18 -0
- moriarty/assets/templates/misconfigurations/cors-misconfiguration.yaml +30 -0
- moriarty/assets/templates/misconfigurations/debug-enabled.yaml +29 -0
- moriarty/assets/templates/misconfigurations/directory-listing.yaml +33 -0
- moriarty/assets/templates/misconfigurations/jwt-none-algo.yaml +30 -0
- moriarty/assets/templates/misconfigurations/ssl-tls-weak.yaml +23 -0
- moriarty/assets/templates/vulnerabilities/lfi-basic.yaml +31 -0
- moriarty/assets/templates/vulnerabilities/open-redirect.yaml +31 -0
- moriarty/assets/templates/vulnerabilities/rce-basic.yaml +34 -0
- moriarty/assets/templates/vulnerabilities/sqli-error.yaml +39 -0
- moriarty/assets/templates/vulnerabilities/ssrf-basic.yaml +31 -0
- moriarty/assets/templates/vulnerabilities/xss-reflected.yaml +38 -0
- moriarty/assets/templates/vulnerabilities/xxe-basic.yaml +30 -0
- moriarty/assets/wordlists/subdomains-1000.txt +1063 -0
- moriarty/cli/__init__.py +3 -0
- moriarty/cli/app.py +120 -0
- moriarty/cli/async_utils.py +19 -0
- moriarty/cli/dns.py +83 -0
- moriarty/cli/domain_cmd.py +572 -0
- moriarty/cli/email.py +383 -0
- moriarty/cli/email_investigate.py +224 -0
- moriarty/cli/intelligence.py +329 -0
- moriarty/cli/output.py +62 -0
- moriarty/cli/rdap.py +94 -0
- moriarty/cli/state.py +38 -0
- moriarty/cli/tls.py +91 -0
- moriarty/cli/user.py +227 -0
- moriarty/core/cache_backend.py +223 -0
- moriarty/core/config_manager.py +303 -0
- moriarty/correlator/__init__.py +0 -0
- moriarty/data/__init__.py +81 -0
- moriarty/data/ioc/__init__.py +142 -0
- moriarty/data/ioc/matcher.py +254 -0
- moriarty/data/ioc/types.py +267 -0
- moriarty/data/local_intelligence.py +507 -0
- moriarty/data/signature_loaders/__init__.py +103 -0
- moriarty/data/signature_loaders/base.py +54 -0
- moriarty/data/signature_loaders/ioc_feed.py +356 -0
- moriarty/data/signature_loaders/wappalyzer.py +112 -0
- moriarty/dsl/__init__.py +0 -0
- moriarty/dsl/loader.py +99 -0
- moriarty/dsl/schema.py +47 -0
- moriarty/export/__init__.py +0 -0
- moriarty/intelligence/__init__.py +27 -0
- moriarty/intelligence/__main__.py +150 -0
- moriarty/intelligence/config.py +395 -0
- moriarty/intelligence/ioc.py +267 -0
- moriarty/intelligence/signatures.py +550 -0
- moriarty/intelligence/storage.py +501 -0
- moriarty/interop/__init__.py +0 -0
- moriarty/logging/__init__.py +0 -0
- moriarty/logging/config.py +47 -0
- moriarty/models/__init__.py +16 -0
- moriarty/models/assertion.py +24 -0
- moriarty/models/entity.py +22 -0
- moriarty/models/evidence.py +37 -0
- moriarty/models/relation.py +24 -0
- moriarty/models/types.py +28 -0
- moriarty/modules/__init__.py +0 -0
- moriarty/modules/avatar_hash.py +184 -0
- moriarty/modules/directory_fuzzer.py +322 -0
- moriarty/modules/dns_scan.py +40 -0
- moriarty/modules/domain_scanner.py +620 -0
- moriarty/modules/email_check.py +98 -0
- moriarty/modules/email_investigate.py +267 -0
- moriarty/modules/email_security.py +274 -0
- moriarty/modules/googlemaps_lookup.py +106 -0
- moriarty/modules/headless_executor.py +201 -0
- moriarty/modules/orchestrator.py +60 -0
- moriarty/modules/passive_recon.py +444 -0
- moriarty/modules/phone_extractor.py +151 -0
- moriarty/modules/pipeline_orchestrator.py +726 -0
- moriarty/modules/port_scanner.py +129 -0
- moriarty/modules/rdap.py +61 -0
- moriarty/modules/rdap_extended.py +188 -0
- moriarty/modules/stealth_mode.py +610 -0
- moriarty/modules/subdomain_discovery.py +595 -0
- moriarty/modules/technology_profiler.py +361 -0
- moriarty/modules/template_executor.py +239 -0
- moriarty/modules/template_scanner.py +1048 -0
- moriarty/modules/tls_scan.py +46 -0
- moriarty/modules/tls_validator.py +188 -0
- moriarty/modules/vuln_scanner.py +483 -0
- moriarty/modules/waf_detector.py +585 -0
- moriarty/modules/wayback_discovery.py +234 -0
- moriarty/modules/web_crawler.py +163 -0
- moriarty/net/__init__.py +0 -0
- moriarty/net/dns_cache.py +175 -0
- moriarty/net/dns_client.py +188 -0
- moriarty/net/rdap_client.py +52 -0
- moriarty/net/smtp_client.py +114 -0
- moriarty/net/tls_client.py +111 -0
- moriarty/parsers/__init__.py +0 -0
- moriarty/parsers/html_parser.py +136 -0
- moriarty/tests/__init__.py +0 -0
- moriarty/tests/test_email_service.py +17 -0
- moriarty/tests/test_models.py +46 -0
- moriarty/tests/test_orchestrator.py +30 -0
- moriarty/tests/test_tls_client.py +18 -0
- moriarty_project-0.1.6.dist-info/METADATA +388 -0
- moriarty_project-0.1.6.dist-info/RECORD +418 -0
- moriarty_project-0.1.6.dist-info/WHEEL +4 -0
- 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"]
|
moriarty/net/__init__.py
ADDED
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
|