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,188 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from time import perf_counter
|
6
|
+
from typing import Any, Dict, List, Optional
|
7
|
+
|
8
|
+
import aiodns
|
9
|
+
import httpx
|
10
|
+
import structlog
|
11
|
+
|
12
|
+
from .dns_cache import DNSCache, get_global_cache
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass(slots=True)
|
16
|
+
class MXRecord:
|
17
|
+
priority: int
|
18
|
+
exchange: str
|
19
|
+
|
20
|
+
|
21
|
+
@dataclass(slots=True)
|
22
|
+
class TXTRecord:
|
23
|
+
text: str
|
24
|
+
|
25
|
+
|
26
|
+
@dataclass(slots=True)
|
27
|
+
class DNSLookupResult:
|
28
|
+
a: List[str]
|
29
|
+
aaaa: List[str]
|
30
|
+
mx: List[MXRecord]
|
31
|
+
txt: List[TXTRecord]
|
32
|
+
spf: List[str]
|
33
|
+
dmarc: List[str]
|
34
|
+
|
35
|
+
|
36
|
+
class DNSClient:
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
timeout: float = 8.0,
|
40
|
+
use_cache: bool = True,
|
41
|
+
doh_endpoint: Optional[str] = None,
|
42
|
+
dot_server: Optional[str] = None,
|
43
|
+
) -> None:
|
44
|
+
self._resolver = aiodns.DNSResolver(timeout=timeout)
|
45
|
+
self._timeout = timeout
|
46
|
+
self._logger = structlog.get_logger(__name__)
|
47
|
+
self._use_cache = use_cache
|
48
|
+
self._cache = get_global_cache() if use_cache else None
|
49
|
+
self._doh_endpoint = doh_endpoint # Ex: https://cloudflare-dns.com/dns-query
|
50
|
+
self._dot_server = dot_server # Ex: 1.1.1.1:853
|
51
|
+
|
52
|
+
if doh_endpoint:
|
53
|
+
self._logger.info("dns.doh.enabled", endpoint=doh_endpoint)
|
54
|
+
if dot_server:
|
55
|
+
self._logger.info("dns.dot.enabled", server=dot_server)
|
56
|
+
|
57
|
+
async def lookup_domain(self, domain: str) -> DNSLookupResult:
|
58
|
+
start = perf_counter()
|
59
|
+
self._logger.info("dns.lookup.start", domain=domain, timeout_s=self._timeout)
|
60
|
+
a_task = asyncio.create_task(self._query(domain, "A"))
|
61
|
+
aaaa_task = asyncio.create_task(self._query(domain, "AAAA"))
|
62
|
+
mx_task = asyncio.create_task(self._query(domain, "MX"))
|
63
|
+
txt_task = asyncio.create_task(self._query(domain, "TXT"))
|
64
|
+
dmarc_task = asyncio.create_task(self._query(f"_dmarc.{domain}", "TXT"))
|
65
|
+
|
66
|
+
a_records = await a_task
|
67
|
+
aaaa_records = await aaaa_task
|
68
|
+
mx_records = await mx_task
|
69
|
+
txt_records = await txt_task
|
70
|
+
dmarc_records = await dmarc_task
|
71
|
+
|
72
|
+
mx_structured = [
|
73
|
+
MXRecord(priority=int(entry.get("priority", 0)), exchange=str(entry.get("host")))
|
74
|
+
for entry in mx_records
|
75
|
+
if "host" in entry
|
76
|
+
]
|
77
|
+
|
78
|
+
txt_structured = [
|
79
|
+
TXTRecord(text=str(entry.get("text", "")))
|
80
|
+
for entry in txt_records
|
81
|
+
if entry.get("text")
|
82
|
+
]
|
83
|
+
|
84
|
+
spf_records = [record.text for record in txt_structured if record.text.lower().startswith("v=spf1")]
|
85
|
+
dmarc_structured = [
|
86
|
+
TXTRecord(text=str(entry.get("text", "")))
|
87
|
+
for entry in dmarc_records
|
88
|
+
if entry.get("text")
|
89
|
+
]
|
90
|
+
|
91
|
+
result = DNSLookupResult(
|
92
|
+
a=[entry.get("host") for entry in a_records if entry.get("host")],
|
93
|
+
aaaa=[entry.get("host") for entry in aaaa_records if entry.get("host")],
|
94
|
+
mx=sorted(mx_structured, key=lambda mx: (mx.priority, mx.exchange)),
|
95
|
+
txt=txt_structured,
|
96
|
+
spf=spf_records,
|
97
|
+
dmarc=[record.text for record in dmarc_structured],
|
98
|
+
)
|
99
|
+
latency_ms = (perf_counter() - start) * 1000
|
100
|
+
self._logger.info(
|
101
|
+
"dns.lookup.success",
|
102
|
+
domain=domain,
|
103
|
+
latency_ms=round(latency_ms, 2),
|
104
|
+
a=len(result.a),
|
105
|
+
aaaa=len(result.aaaa),
|
106
|
+
mx=len(result.mx),
|
107
|
+
)
|
108
|
+
return result
|
109
|
+
|
110
|
+
async def _query(self, domain: str, record_type: str) -> List[Dict[str, Any]]:
|
111
|
+
# Verifica cache primeiro
|
112
|
+
if self._cache:
|
113
|
+
cached = await self._cache.get(domain, record_type)
|
114
|
+
if cached is not None:
|
115
|
+
return cached
|
116
|
+
|
117
|
+
# DoH (DNS-over-HTTPS)
|
118
|
+
if self._doh_endpoint:
|
119
|
+
try:
|
120
|
+
result = await self._query_doh(domain, record_type)
|
121
|
+
if result and self._cache:
|
122
|
+
await self._cache.set(domain, record_type, result, ttl=300)
|
123
|
+
return result
|
124
|
+
except Exception as e:
|
125
|
+
self._logger.warning("dns.doh.error", error=str(e), domain=domain)
|
126
|
+
# Fallback para DNS tradicional
|
127
|
+
|
128
|
+
# DNS tradicional
|
129
|
+
try:
|
130
|
+
result = await self._resolver.query(domain, record_type)
|
131
|
+
except aiodns.error.DNSError:
|
132
|
+
self._logger.warning("dns.lookup.miss", domain=domain, record_type=record_type)
|
133
|
+
return []
|
134
|
+
|
135
|
+
if isinstance(result, list):
|
136
|
+
records = [self._record_to_dict(entry) for entry in result]
|
137
|
+
|
138
|
+
# Salva no cache com TTL
|
139
|
+
if self._cache and records:
|
140
|
+
ttl = getattr(result[0], 'ttl', 300) if result else 300
|
141
|
+
await self._cache.set(domain, record_type, records, ttl=ttl)
|
142
|
+
|
143
|
+
return records
|
144
|
+
|
145
|
+
return []
|
146
|
+
|
147
|
+
async def _query_doh(self, domain: str, record_type: str) -> List[Dict[str, Any]]:
|
148
|
+
"""Consulta DNS via HTTPS (DoH)."""
|
149
|
+
async with httpx.AsyncClient(timeout=self._timeout) as client:
|
150
|
+
response = await client.get(
|
151
|
+
self._doh_endpoint,
|
152
|
+
params={
|
153
|
+
"name": domain,
|
154
|
+
"type": record_type,
|
155
|
+
},
|
156
|
+
headers={"Accept": "application/dns-json"},
|
157
|
+
)
|
158
|
+
response.raise_for_status()
|
159
|
+
data = response.json()
|
160
|
+
|
161
|
+
# Converte resposta DoH para formato compatível
|
162
|
+
answers = data.get("Answer", [])
|
163
|
+
if not answers:
|
164
|
+
return []
|
165
|
+
|
166
|
+
records = []
|
167
|
+
for answer in answers:
|
168
|
+
if record_type == "A" or record_type == "AAAA":
|
169
|
+
records.append({"host": answer.get("data"), "ttl": answer.get("TTL", 300)})
|
170
|
+
elif record_type == "MX":
|
171
|
+
parts = answer.get("data", "").split()
|
172
|
+
if len(parts) == 2:
|
173
|
+
records.append({"priority": int(parts[0]), "host": parts[1], "ttl": answer.get("TTL", 300)})
|
174
|
+
elif record_type == "TXT":
|
175
|
+
records.append({"text": answer.get("data", ""), "ttl": answer.get("TTL", 300)})
|
176
|
+
|
177
|
+
return records
|
178
|
+
|
179
|
+
@staticmethod
|
180
|
+
def _record_to_dict(record: Any) -> Dict[str, Any]:
|
181
|
+
return {
|
182
|
+
key: getattr(record, key)
|
183
|
+
for key in dir(record)
|
184
|
+
if not key.startswith("_") and not callable(getattr(record, key))
|
185
|
+
}
|
186
|
+
|
187
|
+
|
188
|
+
__all__ = ["DNSClient", "DNSLookupResult", "MXRecord", "TXTRecord"]
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from time import perf_counter
|
5
|
+
from typing import Any, Dict
|
6
|
+
|
7
|
+
import httpx
|
8
|
+
import structlog
|
9
|
+
|
10
|
+
logger = structlog.get_logger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass(slots=True)
|
14
|
+
class RDAPResponse:
|
15
|
+
url: str
|
16
|
+
payload: Dict[str, Any]
|
17
|
+
status: int
|
18
|
+
latency_ms: float
|
19
|
+
|
20
|
+
|
21
|
+
class RDAPClient:
|
22
|
+
def __init__(self, base_url: str = "https://rdap.org", timeout: float = 8.0, http2: bool = True) -> None:
|
23
|
+
self._base_url = base_url.rstrip("/")
|
24
|
+
self._timeout = timeout
|
25
|
+
self._http2 = http2
|
26
|
+
|
27
|
+
async def fetch(self, path: str) -> RDAPResponse:
|
28
|
+
url = f"{self._base_url}/{path.lstrip('/')}"
|
29
|
+
logger.info("rdap.request.start", url=url)
|
30
|
+
start = perf_counter()
|
31
|
+
async with httpx.AsyncClient(timeout=self._timeout, http2=self._http2) as client:
|
32
|
+
response = await client.get(url, headers={"Accept": "application/rdap+json, application/json"})
|
33
|
+
latency_ms = (perf_counter() - start) * 1000
|
34
|
+
if response.status_code >= 400:
|
35
|
+
logger.warning(
|
36
|
+
"rdap.request.error",
|
37
|
+
url=url,
|
38
|
+
status=response.status_code,
|
39
|
+
latency_ms=round(latency_ms, 2),
|
40
|
+
)
|
41
|
+
response.raise_for_status()
|
42
|
+
payload = response.json()
|
43
|
+
logger.info(
|
44
|
+
"rdap.request.success",
|
45
|
+
url=url,
|
46
|
+
status=response.status_code,
|
47
|
+
latency_ms=round(latency_ms, 2),
|
48
|
+
)
|
49
|
+
return RDAPResponse(url=url, payload=payload, status=response.status_code, latency_ms=latency_ms)
|
50
|
+
|
51
|
+
|
52
|
+
__all__ = ["RDAPClient", "RDAPResponse"]
|
@@ -0,0 +1,114 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import contextlib
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from time import perf_counter
|
7
|
+
from typing import Iterable, List, Optional
|
8
|
+
|
9
|
+
import aiosmtplib
|
10
|
+
import structlog
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass(slots=True)
|
14
|
+
class SMTPAttempt:
|
15
|
+
host: str
|
16
|
+
port: int
|
17
|
+
result_code: Optional[int]
|
18
|
+
result_message: Optional[str]
|
19
|
+
success: bool
|
20
|
+
error: Optional[str]
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass(slots=True)
|
24
|
+
class SMTPProbeResult:
|
25
|
+
deliverable: bool
|
26
|
+
attempts: List[SMTPAttempt]
|
27
|
+
|
28
|
+
|
29
|
+
class SMTPClient:
|
30
|
+
def __init__(self, timeout: float = 8.0, wait: float = 1.0, retries: int = 1) -> None:
|
31
|
+
self._timeout = timeout
|
32
|
+
self._wait = wait
|
33
|
+
self._retries = retries
|
34
|
+
self._logger = structlog.get_logger(__name__)
|
35
|
+
|
36
|
+
async def probe(self, email: str, from_address: str, hosts: Iterable[str]) -> SMTPProbeResult:
|
37
|
+
attempts: List[SMTPAttempt] = []
|
38
|
+
deliverable = False
|
39
|
+
|
40
|
+
for host in hosts:
|
41
|
+
start = perf_counter()
|
42
|
+
self._logger.info(
|
43
|
+
"smtp.probe.start",
|
44
|
+
host=host,
|
45
|
+
retries=self._retries,
|
46
|
+
timeout_s=self._timeout,
|
47
|
+
)
|
48
|
+
for attempt in range(self._retries + 1):
|
49
|
+
smtp: Optional[aiosmtplib.SMTP] = None
|
50
|
+
try:
|
51
|
+
smtp = aiosmtplib.SMTP(hostname=host, port=25, timeout=self._timeout)
|
52
|
+
await smtp.connect()
|
53
|
+
await smtp.ehlo()
|
54
|
+
await smtp.mail(from_address)
|
55
|
+
rcpt_code, rcpt_message = await smtp.rcpt(email)
|
56
|
+
message_text = rcpt_message.decode("utf-8", errors="replace") if isinstance(rcpt_message, bytes) else str(rcpt_message)
|
57
|
+
success = 200 <= rcpt_code < 300
|
58
|
+
attempts.append(
|
59
|
+
SMTPAttempt(
|
60
|
+
host=host,
|
61
|
+
port=25,
|
62
|
+
result_code=rcpt_code,
|
63
|
+
result_message=message_text,
|
64
|
+
success=success,
|
65
|
+
error=None,
|
66
|
+
)
|
67
|
+
)
|
68
|
+
if success:
|
69
|
+
deliverable = True
|
70
|
+
await smtp.quit()
|
71
|
+
return SMTPProbeResult(deliverable=deliverable, attempts=attempts)
|
72
|
+
except aiosmtplib.errors.SMTPException as exc:
|
73
|
+
attempts.append(
|
74
|
+
SMTPAttempt(
|
75
|
+
host=host,
|
76
|
+
port=25,
|
77
|
+
result_code=None,
|
78
|
+
result_message=None,
|
79
|
+
success=False,
|
80
|
+
error=str(exc),
|
81
|
+
)
|
82
|
+
)
|
83
|
+
except Exception as exc: # noqa: BLE001
|
84
|
+
attempts.append(
|
85
|
+
SMTPAttempt(
|
86
|
+
host=host,
|
87
|
+
port=25,
|
88
|
+
result_code=None,
|
89
|
+
result_message=None,
|
90
|
+
success=False,
|
91
|
+
error=str(exc),
|
92
|
+
)
|
93
|
+
)
|
94
|
+
finally:
|
95
|
+
if smtp is not None:
|
96
|
+
with contextlib.suppress(Exception):
|
97
|
+
await smtp.quit()
|
98
|
+
|
99
|
+
if attempt < self._retries:
|
100
|
+
await asyncio.sleep(self._wait)
|
101
|
+
|
102
|
+
latency_ms = (perf_counter() - start) * 1000
|
103
|
+
self._logger.info(
|
104
|
+
"smtp.probe.complete",
|
105
|
+
host=host,
|
106
|
+
attempts=len(attempts),
|
107
|
+
latency_ms=round(latency_ms, 2),
|
108
|
+
deliverable=deliverable,
|
109
|
+
)
|
110
|
+
|
111
|
+
return SMTPProbeResult(deliverable=deliverable, attempts=attempts)
|
112
|
+
|
113
|
+
|
114
|
+
__all__ = ["SMTPClient", "SMTPProbeResult", "SMTPAttempt"]
|
@@ -0,0 +1,111 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import hashlib
|
5
|
+
import socket
|
6
|
+
import ssl
|
7
|
+
from dataclasses import dataclass
|
8
|
+
from datetime import datetime
|
9
|
+
from time import perf_counter
|
10
|
+
from typing import List, Optional
|
11
|
+
|
12
|
+
import structlog
|
13
|
+
|
14
|
+
logger = structlog.get_logger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass(slots=True)
|
18
|
+
class TLSCertificate:
|
19
|
+
subject: str
|
20
|
+
issuer: str
|
21
|
+
not_before: datetime
|
22
|
+
not_after: datetime
|
23
|
+
fingerprint_sha256: str
|
24
|
+
subject_alt_names: List[str]
|
25
|
+
|
26
|
+
|
27
|
+
@dataclass(slots=True)
|
28
|
+
class TLSInspection:
|
29
|
+
host: str
|
30
|
+
port: int
|
31
|
+
protocol: Optional[str]
|
32
|
+
cipher: Optional[str]
|
33
|
+
certificates: List[TLSCertificate]
|
34
|
+
latency_ms: float
|
35
|
+
|
36
|
+
|
37
|
+
class TLSClient:
|
38
|
+
def __init__(self, timeout: float = 8.0, verify: bool = False) -> None:
|
39
|
+
self._timeout = timeout
|
40
|
+
self._verify = verify
|
41
|
+
|
42
|
+
async def inspect(self, host: str, port: int = 443) -> TLSInspection:
|
43
|
+
logger.info("tls.inspect.start", host=host, port=port)
|
44
|
+
start = perf_counter()
|
45
|
+
try:
|
46
|
+
protocol, cipher, certificates = await asyncio.wait_for(
|
47
|
+
asyncio.to_thread(self._inspect_sync, host, port),
|
48
|
+
timeout=self._timeout,
|
49
|
+
)
|
50
|
+
except Exception:
|
51
|
+
logger.exception("tls.inspect.error", host=host, port=port)
|
52
|
+
raise
|
53
|
+
latency_ms = (perf_counter() - start) * 1000
|
54
|
+
logger.info(
|
55
|
+
"tls.inspect.success",
|
56
|
+
host=host,
|
57
|
+
port=port,
|
58
|
+
latency_ms=round(latency_ms, 2),
|
59
|
+
certificates=len(certificates),
|
60
|
+
)
|
61
|
+
return TLSInspection(
|
62
|
+
host=host,
|
63
|
+
port=port,
|
64
|
+
protocol=protocol,
|
65
|
+
cipher=cipher,
|
66
|
+
certificates=certificates,
|
67
|
+
latency_ms=latency_ms,
|
68
|
+
)
|
69
|
+
|
70
|
+
def _inspect_sync(self, host: str, port: int) -> tuple[Optional[str], Optional[str], List[TLSCertificate]]:
|
71
|
+
context = ssl.create_default_context()
|
72
|
+
if not self._verify:
|
73
|
+
context.check_hostname = False
|
74
|
+
context.verify_mode = ssl.CERT_NONE
|
75
|
+
|
76
|
+
with socket.create_connection((host, port), timeout=self._timeout) as sock:
|
77
|
+
with context.wrap_socket(sock, server_hostname=host) as tls_sock:
|
78
|
+
cipher_info = tls_sock.cipher()
|
79
|
+
protocol = tls_sock.version()
|
80
|
+
der_cert = tls_sock.getpeercert(binary_form=True)
|
81
|
+
cert_dict = tls_sock.getpeercert()
|
82
|
+
|
83
|
+
certificate = self._parse_cert(der_cert, cert_dict)
|
84
|
+
return protocol, cipher_info[0] if cipher_info else None, [certificate]
|
85
|
+
|
86
|
+
def _parse_cert(self, der_bytes: bytes, cert_dict: dict[str, object]) -> TLSCertificate:
|
87
|
+
fingerprint = hashlib.sha256(der_bytes).hexdigest()
|
88
|
+
subject_components = cert_dict.get("subject", [])
|
89
|
+
subject = ", ".join("=".join(component) for rdn in subject_components for component in rdn)
|
90
|
+
issuer_components = cert_dict.get("issuer", [])
|
91
|
+
issuer = ", ".join("=".join(component) for rdn in issuer_components for component in rdn)
|
92
|
+
not_before = self._parse_time(cert_dict.get("notBefore"))
|
93
|
+
not_after = self._parse_time(cert_dict.get("notAfter"))
|
94
|
+
sans = [value for key, value in cert_dict.get("subjectAltName", []) if value]
|
95
|
+
return TLSCertificate(
|
96
|
+
subject=subject,
|
97
|
+
issuer=issuer,
|
98
|
+
not_before=not_before,
|
99
|
+
not_after=not_after,
|
100
|
+
fingerprint_sha256=fingerprint,
|
101
|
+
subject_alt_names=sans,
|
102
|
+
)
|
103
|
+
|
104
|
+
@staticmethod
|
105
|
+
def _parse_time(value: Optional[object]) -> datetime:
|
106
|
+
if isinstance(value, str):
|
107
|
+
return datetime.strptime(value, "%b %d %H:%M:%S %Y %Z")
|
108
|
+
return datetime.fromtimestamp(0)
|
109
|
+
|
110
|
+
|
111
|
+
__all__ = ["TLSClient", "TLSInspection", "TLSCertificate"]
|
File without changes
|
@@ -0,0 +1,136 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import re
|
4
|
+
from typing import Any, List, Optional
|
5
|
+
|
6
|
+
from selectolax.parser import HTMLParser
|
7
|
+
|
8
|
+
from ..dsl.schema import SelectorSpec
|
9
|
+
|
10
|
+
|
11
|
+
class ParserError(Exception):
|
12
|
+
"""Erro ao fazer parsing."""
|
13
|
+
|
14
|
+
|
15
|
+
class HTMLExtractor:
|
16
|
+
"""Extrai dados de HTML usando selectores."""
|
17
|
+
|
18
|
+
def __init__(self, html: str) -> None:
|
19
|
+
self._parser = HTMLParser(html)
|
20
|
+
self._raw_html = html
|
21
|
+
|
22
|
+
def extract(self, selector: SelectorSpec) -> Optional[str]:
|
23
|
+
"""Extrai um único valor usando um seletor."""
|
24
|
+
if selector.type == "css":
|
25
|
+
return self._extract_css(selector)
|
26
|
+
elif selector.type == "xpath":
|
27
|
+
# selectolax não suporta XPath nativamente, fallback para lxml se necessário
|
28
|
+
return self._extract_xpath(selector)
|
29
|
+
elif selector.type == "regex":
|
30
|
+
return self._extract_regex(selector)
|
31
|
+
elif selector.type == "json":
|
32
|
+
# JSON path sobre o HTML bruto
|
33
|
+
return None
|
34
|
+
return None
|
35
|
+
|
36
|
+
def extract_all(self, selector: SelectorSpec) -> List[str]:
|
37
|
+
"""Extrai múltiplos valores."""
|
38
|
+
if selector.type == "css":
|
39
|
+
return self._extract_css_all(selector)
|
40
|
+
elif selector.type == "regex":
|
41
|
+
return self._extract_regex_all(selector)
|
42
|
+
return []
|
43
|
+
|
44
|
+
def exists(self, selector: SelectorSpec) -> bool:
|
45
|
+
"""Verifica se um seletor encontra algo."""
|
46
|
+
result = self.extract(selector)
|
47
|
+
return result is not None and len(result) > 0
|
48
|
+
|
49
|
+
def _extract_css(self, selector: SelectorSpec) -> Optional[str]:
|
50
|
+
"""Extrai usando CSS selector."""
|
51
|
+
node = self._parser.css_first(selector.query)
|
52
|
+
if node is None:
|
53
|
+
return None
|
54
|
+
|
55
|
+
if selector.attribute:
|
56
|
+
return node.attributes.get(selector.attribute)
|
57
|
+
|
58
|
+
return node.text(strip=True)
|
59
|
+
|
60
|
+
def _extract_css_all(self, selector: SelectorSpec) -> List[str]:
|
61
|
+
"""Extrai todos os matches de um CSS selector."""
|
62
|
+
nodes = self._parser.css(selector.query)
|
63
|
+
results: List[str] = []
|
64
|
+
|
65
|
+
for node in nodes:
|
66
|
+
if selector.attribute:
|
67
|
+
value = node.attributes.get(selector.attribute)
|
68
|
+
if value:
|
69
|
+
results.append(value)
|
70
|
+
else:
|
71
|
+
text = node.text(strip=True)
|
72
|
+
if text:
|
73
|
+
results.append(text)
|
74
|
+
|
75
|
+
return results
|
76
|
+
|
77
|
+
def _extract_xpath(self, selector: SelectorSpec) -> Optional[str]:
|
78
|
+
"""Extrai usando XPath (requer lxml)."""
|
79
|
+
try:
|
80
|
+
from lxml import etree, html
|
81
|
+
except ImportError:
|
82
|
+
raise ParserError("lxml required for XPath selectors")
|
83
|
+
|
84
|
+
try:
|
85
|
+
tree = html.fromstring(self._raw_html)
|
86
|
+
result = tree.xpath(selector.query)
|
87
|
+
|
88
|
+
if not result:
|
89
|
+
return None
|
90
|
+
|
91
|
+
if isinstance(result, list) and len(result) > 0:
|
92
|
+
element = result[0]
|
93
|
+
if selector.attribute and hasattr(element, "get"):
|
94
|
+
return element.get(selector.attribute)
|
95
|
+
if hasattr(element, "text"):
|
96
|
+
return element.text
|
97
|
+
return str(element)
|
98
|
+
|
99
|
+
return str(result)
|
100
|
+
except Exception:
|
101
|
+
return None
|
102
|
+
|
103
|
+
def _extract_regex(self, selector: SelectorSpec) -> Optional[str]:
|
104
|
+
"""Extrai usando regex."""
|
105
|
+
pattern = re.compile(selector.query, re.IGNORECASE | re.DOTALL)
|
106
|
+
match = pattern.search(self._raw_html)
|
107
|
+
|
108
|
+
if match:
|
109
|
+
group = selector.group if selector.group is not None else 0
|
110
|
+
try:
|
111
|
+
return match.group(group)
|
112
|
+
except IndexError:
|
113
|
+
return None
|
114
|
+
|
115
|
+
return None
|
116
|
+
|
117
|
+
def _extract_regex_all(self, selector: SelectorSpec) -> List[str]:
|
118
|
+
"""Extrai todos os matches de regex."""
|
119
|
+
pattern = re.compile(selector.query, re.IGNORECASE | re.DOTALL)
|
120
|
+
matches = pattern.finditer(self._raw_html)
|
121
|
+
|
122
|
+
results: List[str] = []
|
123
|
+
group = selector.group if selector.group is not None else 0
|
124
|
+
|
125
|
+
for match in matches:
|
126
|
+
try:
|
127
|
+
value = match.group(group)
|
128
|
+
if value:
|
129
|
+
results.append(value)
|
130
|
+
except IndexError:
|
131
|
+
continue
|
132
|
+
|
133
|
+
return results
|
134
|
+
|
135
|
+
|
136
|
+
__all__ = ["HTMLExtractor", "ParserError"]
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
|
5
|
+
from moriarty.modules.email_check import EmailCheckOptions, EmailCheckService
|
6
|
+
|
7
|
+
|
8
|
+
def test_email_normalization_only() -> None:
|
9
|
+
service = EmailCheckService(
|
10
|
+
email="Alice@Example.COM",
|
11
|
+
options=EmailCheckOptions(check_dns=False, check_smtp=False),
|
12
|
+
timeout=0.1,
|
13
|
+
)
|
14
|
+
result = asyncio.run(service.run())
|
15
|
+
assert result.normalized_email == "alice@example.com"
|
16
|
+
assert result.dns is None
|
17
|
+
assert result.smtp is None
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from moriarty.models import (
|
4
|
+
Assertion,
|
5
|
+
ConfidenceBand,
|
6
|
+
CustodyEvent,
|
7
|
+
Entity,
|
8
|
+
EntityKind,
|
9
|
+
Evidence,
|
10
|
+
EvidenceKind,
|
11
|
+
Relation,
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
def test_entity_creation() -> None:
|
16
|
+
entity = Entity(kind=EntityKind.EMAIL, value="alice@example.com")
|
17
|
+
assert entity.value == "alice@example.com"
|
18
|
+
assert entity.kind is EntityKind.EMAIL
|
19
|
+
|
20
|
+
|
21
|
+
def test_evidence_chain() -> None:
|
22
|
+
evidence = Evidence(
|
23
|
+
kind=EvidenceKind.NETWORK,
|
24
|
+
sha256="deadbeef",
|
25
|
+
source="dns",
|
26
|
+
custody=[CustodyEvent(actor="tester", action="collected")],
|
27
|
+
)
|
28
|
+
assert evidence.custody[0].actor == "tester"
|
29
|
+
|
30
|
+
|
31
|
+
def test_assertion_defaults() -> None:
|
32
|
+
entity = Entity(kind=EntityKind.DOMAIN, value="example.com")
|
33
|
+
assertion = Assertion(
|
34
|
+
subject_id=entity.id,
|
35
|
+
predicate="resolves_to",
|
36
|
+
object="93.184.216.34",
|
37
|
+
)
|
38
|
+
assert assertion.band == ConfidenceBand.INDICATIVE
|
39
|
+
|
40
|
+
|
41
|
+
def test_relation_attributes() -> None:
|
42
|
+
src = Entity(kind=EntityKind.EMAIL, value="alice@example.com")
|
43
|
+
dst = Entity(kind=EntityKind.USERNAME, value="alice")
|
44
|
+
relation = Relation(source_id=src.id, target_id=dst.id, relation_type="aka")
|
45
|
+
assert relation.source_id == src.id
|
46
|
+
assert relation.relation_type == "aka"
|