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,726 @@
|
|
1
|
+
"""Pipeline Orchestrator - Execução declarativa de ataques via YAML."""
|
2
|
+
import asyncio
|
3
|
+
import json
|
4
|
+
import time
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Any, Dict, List, Optional
|
8
|
+
|
9
|
+
import httpx
|
10
|
+
import structlog
|
11
|
+
import yaml
|
12
|
+
from rich.console import Console
|
13
|
+
from rich.tree import Tree
|
14
|
+
|
15
|
+
from moriarty.core.config_manager import config_manager
|
16
|
+
from moriarty.modules.passive_recon import PassiveRecon
|
17
|
+
from moriarty.modules.port_scanner import PortScanner
|
18
|
+
from moriarty.modules.web_crawler import WebCrawler
|
19
|
+
|
20
|
+
logger = structlog.get_logger(__name__)
|
21
|
+
console = Console()
|
22
|
+
|
23
|
+
|
24
|
+
@dataclass
|
25
|
+
class PipelineStage:
|
26
|
+
"""Stage do pipeline."""
|
27
|
+
name: str
|
28
|
+
module: str
|
29
|
+
config: Dict[str, Any]
|
30
|
+
depends_on: List[str] = None
|
31
|
+
condition: Optional[str] = None
|
32
|
+
|
33
|
+
|
34
|
+
@dataclass
|
35
|
+
class PipelineResult:
|
36
|
+
"""Resultado de execução do pipeline."""
|
37
|
+
stage: str
|
38
|
+
success: bool
|
39
|
+
output: Any
|
40
|
+
duration: float
|
41
|
+
|
42
|
+
|
43
|
+
class PipelineOrchestrator:
|
44
|
+
"""
|
45
|
+
Orquestrador de pipelines declarativos.
|
46
|
+
|
47
|
+
Exemplo de pipeline.yaml:
|
48
|
+
|
49
|
+
```yaml
|
50
|
+
name: "Full Recon Pipeline"
|
51
|
+
target: "example.com"
|
52
|
+
|
53
|
+
variables:
|
54
|
+
wordlist: "/path/to/wordlist.txt"
|
55
|
+
threads: 20
|
56
|
+
|
57
|
+
stages:
|
58
|
+
- name: "DNS Discovery"
|
59
|
+
module: "dns"
|
60
|
+
config:
|
61
|
+
records: ["A", "AAAA", "MX", "TXT"]
|
62
|
+
|
63
|
+
- name: "Subdomain Enum"
|
64
|
+
module: "subdiscover"
|
65
|
+
depends_on: ["DNS Discovery"]
|
66
|
+
config:
|
67
|
+
sources: ["crtsh", "wayback"]
|
68
|
+
validate: true
|
69
|
+
|
70
|
+
- name: "Port Scan"
|
71
|
+
module: "ports"
|
72
|
+
depends_on: ["Subdomain Enum"]
|
73
|
+
config:
|
74
|
+
ports: "common"
|
75
|
+
stealth: 2
|
76
|
+
|
77
|
+
- name: "Template Scan"
|
78
|
+
module: "template-scan"
|
79
|
+
depends_on: ["Port Scan"]
|
80
|
+
condition: "len(previous.open_ports) > 0"
|
81
|
+
config:
|
82
|
+
severity: ["critical", "high"]
|
83
|
+
```
|
84
|
+
"""
|
85
|
+
|
86
|
+
def __init__(
|
87
|
+
self,
|
88
|
+
pipeline_file: str,
|
89
|
+
target_override: Optional[str] = None,
|
90
|
+
vars_file: Optional[str] = None,
|
91
|
+
dry_run: bool = False,
|
92
|
+
log_file: Optional[str] = None,
|
93
|
+
checkpoint_file: Optional[str] = None,
|
94
|
+
resume: bool = True,
|
95
|
+
):
|
96
|
+
self.pipeline_file = pipeline_file
|
97
|
+
self.target_override = target_override
|
98
|
+
self.vars_file = vars_file
|
99
|
+
self.dry_run = dry_run
|
100
|
+
self.resume_enabled = resume
|
101
|
+
|
102
|
+
self.pipeline_data: Dict[str, Any] = {}
|
103
|
+
self.stages: List[PipelineStage] = []
|
104
|
+
self.results: List[PipelineResult] = []
|
105
|
+
self.variables: Dict[str, Any] = {}
|
106
|
+
self.stage_results: Dict[str, PipelineResult] = {}
|
107
|
+
self.default_retries: int = 1
|
108
|
+
self.config = config_manager
|
109
|
+
self.notification_config = getattr(self.config, "notifications", None) if self.config else None
|
110
|
+
|
111
|
+
base_dir = Path(self.config.config_dir) if self.config else Path.home() / ".moriarty"
|
112
|
+
|
113
|
+
logs_dir = base_dir / "logs"
|
114
|
+
checkpoints_dir = base_dir / "checkpoints"
|
115
|
+
logs_dir.mkdir(parents=True, exist_ok=True)
|
116
|
+
checkpoints_dir.mkdir(parents=True, exist_ok=True)
|
117
|
+
|
118
|
+
self.log_file_path = Path(log_file) if log_file else logs_dir / "pipeline.log"
|
119
|
+
self.checkpoint_path = (
|
120
|
+
Path(checkpoint_file)
|
121
|
+
if checkpoint_file
|
122
|
+
else checkpoints_dir / f"{Path(self.pipeline_file).stem}.json"
|
123
|
+
)
|
124
|
+
|
125
|
+
async def run(self):
|
126
|
+
"""Executa pipeline."""
|
127
|
+
logger.info("pipeline.start", file=self.pipeline_file)
|
128
|
+
self._log_event("pipeline_start", file=self.pipeline_file)
|
129
|
+
|
130
|
+
# Carrega pipeline
|
131
|
+
self._load_pipeline()
|
132
|
+
|
133
|
+
# Carrega variáveis
|
134
|
+
self._load_variables()
|
135
|
+
|
136
|
+
if self.resume_enabled:
|
137
|
+
self._load_checkpoint()
|
138
|
+
|
139
|
+
# Mostra resumo
|
140
|
+
self._show_summary()
|
141
|
+
|
142
|
+
if self.dry_run:
|
143
|
+
console.print("[yellow]🔍 Dry run - não executando stages[/yellow]")
|
144
|
+
return
|
145
|
+
|
146
|
+
# Executa stages em ordem
|
147
|
+
await self._execute_stages()
|
148
|
+
|
149
|
+
# Mostra resultados
|
150
|
+
self._show_results()
|
151
|
+
await self._notify_pipeline_summary()
|
152
|
+
self._finalize_checkpoint()
|
153
|
+
self._log_event(
|
154
|
+
"pipeline_complete",
|
155
|
+
success=all(result.success for result in self.results) if self.results else True,
|
156
|
+
total=len(self.results),
|
157
|
+
)
|
158
|
+
|
159
|
+
def _load_pipeline(self):
|
160
|
+
"""Carrega arquivo de pipeline."""
|
161
|
+
try:
|
162
|
+
with open(self.pipeline_file, 'r') as f:
|
163
|
+
self.pipeline_data = yaml.safe_load(f)
|
164
|
+
|
165
|
+
# Override target se fornecido
|
166
|
+
if self.target_override:
|
167
|
+
self.pipeline_data["target"] = self.target_override
|
168
|
+
|
169
|
+
# Parse stages
|
170
|
+
for stage_data in self.pipeline_data.get("stages", []):
|
171
|
+
stage = PipelineStage(
|
172
|
+
name=stage_data["name"],
|
173
|
+
module=stage_data["module"],
|
174
|
+
config=stage_data.get("config", {}),
|
175
|
+
depends_on=stage_data.get("depends_on", []),
|
176
|
+
condition=stage_data.get("condition"),
|
177
|
+
)
|
178
|
+
self.stages.append(stage)
|
179
|
+
|
180
|
+
logger.info("pipeline.loaded", stages=len(self.stages))
|
181
|
+
|
182
|
+
except Exception as e:
|
183
|
+
logger.error("pipeline.load.error", error=str(e))
|
184
|
+
raise
|
185
|
+
|
186
|
+
def _load_variables(self):
|
187
|
+
"""Carrega variáveis do pipeline e arquivo externo."""
|
188
|
+
# Variáveis do pipeline
|
189
|
+
self.variables = self.pipeline_data.get("variables", {})
|
190
|
+
|
191
|
+
# Variáveis de arquivo externo
|
192
|
+
if self.vars_file:
|
193
|
+
try:
|
194
|
+
with open(self.vars_file, 'r') as f:
|
195
|
+
external_vars = yaml.safe_load(f)
|
196
|
+
self.variables.update(external_vars)
|
197
|
+
except Exception as e:
|
198
|
+
logger.warning("pipeline.vars.load.error", file=self.vars_file, error=str(e))
|
199
|
+
|
200
|
+
def _show_summary(self):
|
201
|
+
"""Mostra resumo do pipeline."""
|
202
|
+
tree = Tree(f"[bold cyan]🔄 Pipeline: {self.pipeline_data.get('name', 'Unnamed')}[/bold cyan]")
|
203
|
+
|
204
|
+
tree.add(f"[dim]Target:[/dim] {self.pipeline_data.get('target', 'N/A')}")
|
205
|
+
tree.add(f"[dim]Stages:[/dim] {len(self.stages)}")
|
206
|
+
|
207
|
+
if self.variables:
|
208
|
+
vars_node = tree.add("[dim]Variables:[/dim]")
|
209
|
+
for key, value in self.variables.items():
|
210
|
+
vars_node.add(f"{key}: {value}")
|
211
|
+
|
212
|
+
stages_node = tree.add("[bold]Stages:[/bold]")
|
213
|
+
for stage in self.stages:
|
214
|
+
stage_label = f"{stage.name} [dim]({stage.module})[/dim]"
|
215
|
+
stage_node = stages_node.add(stage_label)
|
216
|
+
|
217
|
+
if stage.depends_on:
|
218
|
+
stage_node.add(f"[dim]Depends on: {', '.join(stage.depends_on)}[/dim]")
|
219
|
+
|
220
|
+
if stage.condition:
|
221
|
+
stage_node.add(f"[dim]Condition: {stage.condition}[/dim]")
|
222
|
+
|
223
|
+
console.print(tree)
|
224
|
+
console.print()
|
225
|
+
|
226
|
+
async def _execute_stages(self):
|
227
|
+
"""Executa stages do pipeline com paralelismo e retries."""
|
228
|
+
pending = {
|
229
|
+
stage.name: stage
|
230
|
+
for stage in self.stages
|
231
|
+
if stage.name not in self.stage_results
|
232
|
+
or not self.stage_results[stage.name].success
|
233
|
+
}
|
234
|
+
running: Dict[asyncio.Task, PipelineStage] = {}
|
235
|
+
|
236
|
+
while pending or running:
|
237
|
+
ready: List[PipelineStage] = []
|
238
|
+
|
239
|
+
for stage_name, stage in list(pending.items()):
|
240
|
+
if not self._dependencies_met(stage):
|
241
|
+
continue
|
242
|
+
|
243
|
+
if stage.condition and not self._evaluate_condition(stage):
|
244
|
+
logger.info("pipeline.stage.skip.condition", stage=stage.name)
|
245
|
+
result = PipelineResult(
|
246
|
+
stage=stage.name,
|
247
|
+
success=True,
|
248
|
+
output="skipped (condition)",
|
249
|
+
duration=0.0,
|
250
|
+
)
|
251
|
+
self.results.append(result)
|
252
|
+
self.stage_results[stage.name] = result
|
253
|
+
pending.pop(stage_name)
|
254
|
+
continue
|
255
|
+
|
256
|
+
ready.append(stage)
|
257
|
+
pending.pop(stage_name)
|
258
|
+
|
259
|
+
for stage in ready:
|
260
|
+
task = asyncio.create_task(self._execute_stage_with_retry(stage))
|
261
|
+
running[task] = stage
|
262
|
+
|
263
|
+
if not running:
|
264
|
+
# Nenhuma stage pronta (dependências não satisfeitas)
|
265
|
+
break
|
266
|
+
|
267
|
+
done, _ = await asyncio.wait(running.keys(), return_when=asyncio.FIRST_COMPLETED)
|
268
|
+
|
269
|
+
for task in done:
|
270
|
+
stage = running.pop(task)
|
271
|
+
try:
|
272
|
+
result = await task
|
273
|
+
except Exception as exc: # pragma: no cover - fallback seguro
|
274
|
+
logger.error("pipeline.stage.unhandled", stage=stage.name, error=str(exc))
|
275
|
+
result = PipelineResult(
|
276
|
+
stage=stage.name,
|
277
|
+
success=False,
|
278
|
+
output=str(exc),
|
279
|
+
duration=0.0,
|
280
|
+
)
|
281
|
+
|
282
|
+
self.results.append(result)
|
283
|
+
self.stage_results[stage.name] = result
|
284
|
+
self._persist_checkpoint(result)
|
285
|
+
|
286
|
+
if result.success:
|
287
|
+
console.print(f"[green]✓ Completo:[/green] {stage.name} [{result.duration:.2f}s]")
|
288
|
+
else:
|
289
|
+
console.print(f"[red]✗ Falhou:[/red] {stage.name}")
|
290
|
+
|
291
|
+
for stage_name, stage in pending.items():
|
292
|
+
logger.warning("pipeline.stage.unexecuted", stage=stage.name)
|
293
|
+
|
294
|
+
def _dependencies_met(self, stage: PipelineStage) -> bool:
|
295
|
+
"""Verifica se dependências do stage já foram executadas."""
|
296
|
+
if not stage.depends_on:
|
297
|
+
return True
|
298
|
+
return all(dep in self.stage_results for dep in stage.depends_on)
|
299
|
+
|
300
|
+
async def _execute_stage_with_retry(self, stage: PipelineStage) -> PipelineResult:
|
301
|
+
"""Executa stage com política de retry."""
|
302
|
+
retries = int(stage.config.get("retries", self.default_retries))
|
303
|
+
delay = float(stage.config.get("retry_delay", 2.0))
|
304
|
+
attempt = 0
|
305
|
+
last_result: Optional[PipelineResult] = None
|
306
|
+
|
307
|
+
while attempt <= retries:
|
308
|
+
await self._notify_stage_start(stage, attempt)
|
309
|
+
self._log_event("stage_start", stage=stage.name, attempt=attempt)
|
310
|
+
result = await self._execute_stage(stage, attempt=attempt)
|
311
|
+
|
312
|
+
if result.success:
|
313
|
+
await self._notify_stage_end(stage, result)
|
314
|
+
self._log_event(
|
315
|
+
"stage_complete",
|
316
|
+
stage=stage.name,
|
317
|
+
success=True,
|
318
|
+
duration=result.duration,
|
319
|
+
)
|
320
|
+
return result
|
321
|
+
|
322
|
+
last_result = result
|
323
|
+
attempt += 1
|
324
|
+
|
325
|
+
if attempt <= retries:
|
326
|
+
logger.warning("pipeline.stage.retry", stage=stage.name, attempt=attempt)
|
327
|
+
await asyncio.sleep(delay)
|
328
|
+
|
329
|
+
await self._notify_stage_end(stage, last_result or result)
|
330
|
+
self._log_event(
|
331
|
+
"stage_complete",
|
332
|
+
stage=stage.name,
|
333
|
+
success=False,
|
334
|
+
duration=(last_result or result).duration if (last_result or result) else 0.0,
|
335
|
+
)
|
336
|
+
return last_result or result
|
337
|
+
|
338
|
+
async def _execute_stage(self, stage: PipelineStage, attempt: int = 0) -> PipelineResult:
|
339
|
+
"""Executa uma stage."""
|
340
|
+
import time
|
341
|
+
start = time.time()
|
342
|
+
|
343
|
+
try:
|
344
|
+
label = stage.name if attempt == 0 else f"{stage.name} (retry {attempt})"
|
345
|
+
console.print(f"[cyan]▶ Executando:[/cyan] {label}")
|
346
|
+
|
347
|
+
# Substitui variáveis no config
|
348
|
+
config = self._substitute_variables(stage.config)
|
349
|
+
|
350
|
+
# Mapeamento de módulos
|
351
|
+
if stage.module == "dns":
|
352
|
+
output = await self._run_dns(config)
|
353
|
+
elif stage.module == "subdiscover":
|
354
|
+
output = await self._run_subdiscover(config)
|
355
|
+
elif stage.module == "ports":
|
356
|
+
output = await self._run_ports(config)
|
357
|
+
elif stage.module == "template-scan":
|
358
|
+
output = await self._run_template_scan(config)
|
359
|
+
elif stage.module == "wayback":
|
360
|
+
output = await self._run_wayback(config)
|
361
|
+
elif stage.module == "passive":
|
362
|
+
output = await self._run_passive(config)
|
363
|
+
elif stage.module == "crawl":
|
364
|
+
output = await self._run_crawl(config)
|
365
|
+
else:
|
366
|
+
logger.warning("pipeline.stage.unknown", module=stage.module)
|
367
|
+
output = None
|
368
|
+
|
369
|
+
duration = time.time() - start
|
370
|
+
|
371
|
+
return PipelineResult(
|
372
|
+
stage=stage.name,
|
373
|
+
success=True,
|
374
|
+
output=output,
|
375
|
+
duration=duration,
|
376
|
+
)
|
377
|
+
|
378
|
+
except Exception as e:
|
379
|
+
logger.error("pipeline.stage.error", stage=stage.name, error=str(e))
|
380
|
+
duration = time.time() - start
|
381
|
+
|
382
|
+
return PipelineResult(
|
383
|
+
stage=stage.name,
|
384
|
+
success=False,
|
385
|
+
output=str(e),
|
386
|
+
duration=duration,
|
387
|
+
)
|
388
|
+
|
389
|
+
def _substitute_variables(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
390
|
+
"""Substitui variáveis no config."""
|
391
|
+
import re
|
392
|
+
|
393
|
+
def replace_var(match):
|
394
|
+
var_name = match.group(1)
|
395
|
+
return str(self.variables.get(var_name, match.group(0)))
|
396
|
+
|
397
|
+
# Converte para string, substitui, converte de volta
|
398
|
+
config_str = str(config)
|
399
|
+
config_str = re.sub(r'\$\{(\w+)\}', replace_var, config_str)
|
400
|
+
|
401
|
+
# Eval seguro (simplificado)
|
402
|
+
try:
|
403
|
+
return eval(config_str)
|
404
|
+
except:
|
405
|
+
return config
|
406
|
+
|
407
|
+
def _evaluate_condition(self, stage: PipelineStage) -> bool:
|
408
|
+
"""Avalia condição booleana do stage."""
|
409
|
+
if not stage.condition:
|
410
|
+
return True
|
411
|
+
|
412
|
+
try:
|
413
|
+
previous = None
|
414
|
+
if self.stage_results:
|
415
|
+
previous = self.stage_results[next(reversed(self.stage_results))]
|
416
|
+
|
417
|
+
context = {
|
418
|
+
"len": len,
|
419
|
+
"any": any,
|
420
|
+
"all": all,
|
421
|
+
"previous": previous,
|
422
|
+
"results": self.stage_results,
|
423
|
+
"stage_result": lambda name: self.stage_results.get(name),
|
424
|
+
}
|
425
|
+
|
426
|
+
return bool(eval(stage.condition, {"__builtins__": {}}, context))
|
427
|
+
except Exception as e:
|
428
|
+
logger.warning("pipeline.stage.condition_error", stage=stage.name, error=str(e))
|
429
|
+
return False
|
430
|
+
|
431
|
+
def _notifications_enabled(self) -> bool:
|
432
|
+
"""Retorna se notificações estão habilitadas."""
|
433
|
+
return bool(
|
434
|
+
self.notification_config
|
435
|
+
and getattr(self.notification_config, "enabled", False)
|
436
|
+
and (
|
437
|
+
getattr(self.notification_config, "slack_enabled", False)
|
438
|
+
or getattr(self.notification_config, "discord_enabled", False)
|
439
|
+
or getattr(self.notification_config, "telegram_enabled", False)
|
440
|
+
)
|
441
|
+
)
|
442
|
+
|
443
|
+
async def _notify_stage_start(self, stage: PipelineStage, attempt: int) -> None:
|
444
|
+
"""Envia notificação de início de stage."""
|
445
|
+
if not self._notifications_enabled():
|
446
|
+
return
|
447
|
+
|
448
|
+
message = f"Stage '{stage.name}' iniciado"
|
449
|
+
if attempt:
|
450
|
+
message += f" (tentativa {attempt + 1})"
|
451
|
+
|
452
|
+
await self._send_notification(message, level="info")
|
453
|
+
|
454
|
+
async def _notify_stage_end(self, stage: PipelineStage, result: PipelineResult) -> None:
|
455
|
+
"""Envia notificação de conclusão de stage."""
|
456
|
+
if not self._notifications_enabled():
|
457
|
+
return
|
458
|
+
|
459
|
+
status = "sucesso" if result and result.success else "falha"
|
460
|
+
duration = f"{result.duration:.2f}s" if result else "0s"
|
461
|
+
snippet = ""
|
462
|
+
if result and isinstance(result.output, str) and result.output:
|
463
|
+
snippet = f" - {result.output[:120]}"
|
464
|
+
|
465
|
+
message = f"Stage '{stage.name}' finalizada com {status} em {duration}{snippet}"
|
466
|
+
level = "success" if result and result.success else "error"
|
467
|
+
await self._send_notification(message, level=level)
|
468
|
+
|
469
|
+
async def _notify_pipeline_summary(self) -> None:
|
470
|
+
"""Envia resumo final do pipeline."""
|
471
|
+
if not self._notifications_enabled():
|
472
|
+
return
|
473
|
+
|
474
|
+
total = len(self.results)
|
475
|
+
success = sum(1 for item in self.results if item.success)
|
476
|
+
message = (
|
477
|
+
f"Pipeline '{self.pipeline_data.get('name', 'Unnamed')}' finalizado: "
|
478
|
+
f"{success}/{total} stages com sucesso."
|
479
|
+
)
|
480
|
+
await self._send_notification(message, level="info")
|
481
|
+
|
482
|
+
async def _send_notification(self, message: str, level: str = "info") -> None:
|
483
|
+
"""Envio de notificações para canais configurados."""
|
484
|
+
if not self._notifications_enabled():
|
485
|
+
return
|
486
|
+
|
487
|
+
notif = self.notification_config
|
488
|
+
title = self.pipeline_data.get('name', 'Moriarty Pipeline')
|
489
|
+
tasks = []
|
490
|
+
|
491
|
+
async with httpx.AsyncClient(timeout=5.0) as client:
|
492
|
+
if getattr(notif, "slack_enabled", False) and notif.slack_webhook:
|
493
|
+
tasks.append(
|
494
|
+
client.post(
|
495
|
+
notif.slack_webhook,
|
496
|
+
json={"text": f"[{level.upper()}] {title}: {message}"},
|
497
|
+
)
|
498
|
+
)
|
499
|
+
|
500
|
+
if getattr(notif, "discord_enabled", False) and notif.discord_webhook:
|
501
|
+
tasks.append(
|
502
|
+
client.post(
|
503
|
+
notif.discord_webhook,
|
504
|
+
json={"content": f"[{level.upper()}] {title}: {message}"},
|
505
|
+
)
|
506
|
+
)
|
507
|
+
|
508
|
+
if (
|
509
|
+
getattr(notif, "telegram_enabled", False)
|
510
|
+
and notif.telegram_token
|
511
|
+
and notif.telegram_chat_id
|
512
|
+
):
|
513
|
+
telegram_url = f"https://api.telegram.org/bot{notif.telegram_token}/sendMessage"
|
514
|
+
tasks.append(
|
515
|
+
client.post(
|
516
|
+
telegram_url,
|
517
|
+
data={
|
518
|
+
"chat_id": notif.telegram_chat_id,
|
519
|
+
"text": f"[{level.upper()}] {title}: {message}",
|
520
|
+
},
|
521
|
+
)
|
522
|
+
)
|
523
|
+
|
524
|
+
if not tasks:
|
525
|
+
return
|
526
|
+
|
527
|
+
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
528
|
+
|
529
|
+
for response in responses:
|
530
|
+
if isinstance(response, Exception):
|
531
|
+
logger.debug("pipeline.notify.error", error=str(response))
|
532
|
+
elif getattr(response, "status_code", 200) >= 400:
|
533
|
+
body = getattr(response, "text", "")
|
534
|
+
logger.debug(
|
535
|
+
"pipeline.notify.http_error",
|
536
|
+
status=response.status_code,
|
537
|
+
body=body[:200],
|
538
|
+
)
|
539
|
+
|
540
|
+
def _load_checkpoint(self) -> None:
|
541
|
+
if not self.resume_enabled or not self.checkpoint_path.exists():
|
542
|
+
return
|
543
|
+
|
544
|
+
try:
|
545
|
+
with self.checkpoint_path.open("r", encoding="utf-8") as handle:
|
546
|
+
data = json.load(handle)
|
547
|
+
|
548
|
+
for entry in data.get("stages", []):
|
549
|
+
stage_name = entry.get("stage")
|
550
|
+
if not stage_name:
|
551
|
+
continue
|
552
|
+
result = PipelineResult(
|
553
|
+
stage=stage_name,
|
554
|
+
success=bool(entry.get("success")),
|
555
|
+
output=entry.get("output"),
|
556
|
+
duration=float(entry.get("duration", 0.0)),
|
557
|
+
)
|
558
|
+
self.stage_results[stage_name] = result
|
559
|
+
if result.success:
|
560
|
+
self.results.append(result)
|
561
|
+
|
562
|
+
if self.stage_results:
|
563
|
+
self._log_event(
|
564
|
+
"checkpoint_loaded",
|
565
|
+
stages=len(self.stage_results),
|
566
|
+
path=str(self.checkpoint_path),
|
567
|
+
)
|
568
|
+
|
569
|
+
except Exception as exc:
|
570
|
+
logger.warning("pipeline.checkpoint.load_error", error=str(exc))
|
571
|
+
|
572
|
+
def _persist_checkpoint(self, result: PipelineResult) -> None:
|
573
|
+
if not self.resume_enabled:
|
574
|
+
return
|
575
|
+
|
576
|
+
payload = {
|
577
|
+
"pipeline": self.pipeline_data.get("name", "Unnamed"),
|
578
|
+
"stages": [
|
579
|
+
{
|
580
|
+
"stage": r.stage,
|
581
|
+
"success": r.success,
|
582
|
+
"duration": r.duration,
|
583
|
+
"output": self._summarize_output(r.output),
|
584
|
+
}
|
585
|
+
for r in self.stage_results.values()
|
586
|
+
],
|
587
|
+
}
|
588
|
+
|
589
|
+
try:
|
590
|
+
with self.checkpoint_path.open("w", encoding="utf-8") as handle:
|
591
|
+
json.dump(payload, handle, ensure_ascii=False, indent=2)
|
592
|
+
except Exception as exc:
|
593
|
+
logger.warning("pipeline.checkpoint.save_error", error=str(exc))
|
594
|
+
|
595
|
+
def _finalize_checkpoint(self) -> None:
|
596
|
+
if not self.resume_enabled:
|
597
|
+
return
|
598
|
+
|
599
|
+
all_success = all(result.success for result in self.results) if self.results else False
|
600
|
+
if all_success and self.checkpoint_path.exists():
|
601
|
+
try:
|
602
|
+
self.checkpoint_path.unlink()
|
603
|
+
self._log_event("checkpoint_cleared", path=str(self.checkpoint_path))
|
604
|
+
except Exception as exc:
|
605
|
+
logger.debug("pipeline.checkpoint.cleanup_error", error=str(exc))
|
606
|
+
|
607
|
+
def _summarize_output(self, output: Any) -> Any:
|
608
|
+
if output is None:
|
609
|
+
return None
|
610
|
+
if isinstance(output, (str, int, float, bool)):
|
611
|
+
text = str(output)
|
612
|
+
else:
|
613
|
+
text = str(output)
|
614
|
+
return text[:2000]
|
615
|
+
|
616
|
+
def _log_event(self, event: str, **payload: Any) -> None:
|
617
|
+
if not self.log_file_path:
|
618
|
+
return
|
619
|
+
entry = {
|
620
|
+
"event": event,
|
621
|
+
"timestamp": time.time(),
|
622
|
+
**payload,
|
623
|
+
}
|
624
|
+
try:
|
625
|
+
with self.log_file_path.open("a", encoding="utf-8") as handle:
|
626
|
+
handle.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
627
|
+
except Exception as exc:
|
628
|
+
logger.debug("pipeline.log.error", error=str(exc))
|
629
|
+
|
630
|
+
async def _run_dns(self, config: Dict[str, Any]) -> Any:
|
631
|
+
"""Executa módulo DNS."""
|
632
|
+
from moriarty.net.dns_client import DNSClient
|
633
|
+
|
634
|
+
client = DNSClient()
|
635
|
+
result = await client.lookup_domain(self.pipeline_data["target"])
|
636
|
+
return result
|
637
|
+
|
638
|
+
async def _run_subdiscover(self, config: Dict[str, Any]) -> Any:
|
639
|
+
"""Executa subdomain discovery."""
|
640
|
+
from moriarty.modules.subdomain_discovery import SubdomainDiscovery
|
641
|
+
|
642
|
+
discovery = SubdomainDiscovery(
|
643
|
+
domain=self.pipeline_data["target"],
|
644
|
+
sources=config.get("sources"),
|
645
|
+
validate=config.get("validate", True),
|
646
|
+
)
|
647
|
+
return await discovery.discover()
|
648
|
+
|
649
|
+
async def _run_ports(self, config: Dict[str, Any]) -> Any:
|
650
|
+
"""Executa port scan."""
|
651
|
+
profile = config.get("profile", "quick")
|
652
|
+
concurrency = int(config.get("concurrency", 200))
|
653
|
+
timeout = float(config.get("timeout", 1.5))
|
654
|
+
|
655
|
+
scanner = PortScanner(
|
656
|
+
target=self.pipeline_data["target"],
|
657
|
+
profile=profile,
|
658
|
+
concurrency=concurrency,
|
659
|
+
timeout=timeout,
|
660
|
+
)
|
661
|
+
results = await scanner.scan()
|
662
|
+
return {
|
663
|
+
"profile": profile,
|
664
|
+
"results": [r.__dict__ for r in results],
|
665
|
+
}
|
666
|
+
|
667
|
+
async def _run_template_scan(self, config: Dict[str, Any]) -> Any:
|
668
|
+
"""Executa template scan."""
|
669
|
+
from moriarty.modules.template_scanner import TemplateScanner
|
670
|
+
|
671
|
+
scanner = TemplateScanner(
|
672
|
+
target=self.pipeline_data["target"],
|
673
|
+
severity_filter=config.get("severity"),
|
674
|
+
)
|
675
|
+
return await scanner.scan()
|
676
|
+
|
677
|
+
async def _run_wayback(self, config: Dict[str, Any]) -> Any:
|
678
|
+
"""Executa wayback discovery."""
|
679
|
+
from moriarty.modules.wayback_discovery import WaybackDiscovery
|
680
|
+
|
681
|
+
wayback = WaybackDiscovery(
|
682
|
+
domain=self.pipeline_data["target"],
|
683
|
+
filter_extensions=config.get("filter_extensions"),
|
684
|
+
filter_status_codes=config.get("filter_status_codes"),
|
685
|
+
)
|
686
|
+
return await wayback.discover()
|
687
|
+
|
688
|
+
async def _run_passive(self, config: Dict[str, Any]) -> Any:
|
689
|
+
"""Executa coleta passiva utilizando PassiveRecon."""
|
690
|
+
recon = PassiveRecon(self.pipeline_data["target"], timeout=float(config.get("timeout", 15.0)))
|
691
|
+
try:
|
692
|
+
result = await recon.collect()
|
693
|
+
return result.to_dict()
|
694
|
+
finally:
|
695
|
+
await recon.close()
|
696
|
+
|
697
|
+
async def _run_crawl(self, config: Dict[str, Any]) -> Any:
|
698
|
+
"""Executa crawler leve."""
|
699
|
+
base_url = config.get("base_url") or f"https://{self.pipeline_data['target']}"
|
700
|
+
crawler = WebCrawler(
|
701
|
+
base_url=base_url,
|
702
|
+
max_pages=int(config.get("max_pages", 100)),
|
703
|
+
max_depth=int(config.get("max_depth", 2)),
|
704
|
+
concurrency=int(config.get("concurrency", 10)),
|
705
|
+
follow_subdomains=bool(config.get("follow_subdomains", False)),
|
706
|
+
)
|
707
|
+
try:
|
708
|
+
pages = await crawler.crawl()
|
709
|
+
return {url: page.__dict__ for url, page in pages.items()}
|
710
|
+
finally:
|
711
|
+
await crawler.close()
|
712
|
+
|
713
|
+
def _show_results(self):
|
714
|
+
"""Mostra resultados do pipeline."""
|
715
|
+
console.print("\n[bold]📊 Resultados:[/bold]\n")
|
716
|
+
|
717
|
+
for result in self.results:
|
718
|
+
status = "✓" if result.success else "✗"
|
719
|
+
color = "green" if result.success else "red"
|
720
|
+
console.print(f"[{color}]{status} {result.stage}[/{color}] - {result.duration:.2f}s")
|
721
|
+
|
722
|
+
successful = sum(1 for r in self.results if r.success)
|
723
|
+
console.print(f"\n[cyan]Total:[/cyan] {successful}/{len(self.results)} stages bem-sucedidos")
|
724
|
+
|
725
|
+
|
726
|
+
__all__ = ["PipelineOrchestrator", "PipelineStage", "PipelineResult"]
|