Skip to content

Commit

Permalink
feat: added Project Discovery Provider (#158)
Browse files Browse the repository at this point in the history
* feat: added Project Discovery Provider

* feat: fixed some issues

* chore: ran black

* feat: fixed typo

* feat: removed unecessary comment

* feat: fixed readme

* feat: fixed readme

---------

Co-authored-by: Victoria Kotiwcki <[email protected]>
  • Loading branch information
SimonGurney and VKotwicki authored Jul 19, 2023
1 parent cc1ac73 commit 581a2ff
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ digitalocean:
--do-domains DO_DOMAINS
Optional
projectdiscovery:
Scan multiple domains by fetching them from Project Discovery
--pd-api-key PD_API_KEY
Required
--pd-domains PD_DOMAINS
Required
file:
Read domains from a file (or folder of files), one per line
Expand Down
70 changes: 70 additions & 0 deletions providers/projectdiscovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import requests, logging, json

from domain import Domain

description = "Scan multiple domains by fetching them from ProjectDiscovery"


class DomainNotFoundError(Exception):
def __init__(self, domain):
self.message = "Domain not found: " + domain
super().__init__(self.message)


class PDApi:
def __init__(self, api_key):
self.session = requests.session()
self.session.headers.update(
{"Content-Type": "application/json", "Authorization": api_key}
)

@staticmethod
def check_response(response: requests.Response):
if response.status_code == 401:
raise ValueError("Invalid API key specified.")

if response.status_code < 200 or response.status_code >= 300:
raise ValueError("Invalid response received from API: " + response.json())

return response

def make_request(self, endpoint):
return self.session.prepare_request(
requests.Request("GET", "https://dns.projectdiscovery.io/dns/" + endpoint)
)

def list_domains(self):
req = self.make_request("domains")

return self.check_response(self.session.send(req))

def get_subdomains(self, domain):
req = self.make_request(f"{domain}/subdomains")
res = self.session.send(req)

if 404 == res.status_code:
raise DomainNotFoundError(domain)

return self.check_response(res)


def fetch_domains(pd_api_key: str, pd_domains: str = None, **args):
root_domains = []
domains = []
api = PDApi(pd_api_key)

if pd_domains is not None and len(pd_domains):
root_domains = [domain.strip(" ") for domain in pd_domains.split(",")]
else:
print("Domain required")
exit()

for domain in root_domains:
if "" == domain or domain is None:
continue

raw_domains = api.get_subdomains(domain).json()
logging.info("Testing", len(raw_domains["subdomains"]), "subdomains")
domains.extend([Domain(f"{sb}.{domain}") for sb in raw_domains["subdomains"]])

return domains

0 comments on commit 581a2ff

Please sign in to comment.