-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1d6b212
commit d0920c4
Showing
8 changed files
with
629 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
"""add_scan_profiles.py: create one or more scan profiles in a given Detectify team | ||
This version of the script DOES NOT avoid creating duplicate scan profiles. Please check your list of domains before | ||
running this script! | ||
The API key permissions required by this script are the following: | ||
- Allow creating scan profiles | ||
- Allow listing domains | ||
Usage: add_scan_profiles.py [-h] [-d [DOMAIN [DOMAIN ...]]] [-f FILE] key | ||
""" | ||
|
||
import argparse | ||
import json | ||
import requests | ||
|
||
API_BASE_URL = 'https://api.detectify.com/rest' | ||
|
||
|
||
def create_scan_profile(assets: dict, asset_name: str, key: str) -> None: | ||
"""Create an Application Scan profile for a given domain | ||
:param assets: A dictionary of assets and tokens | ||
:param asset_name: The name of the asset associated with the given token | ||
:param key: A valid Detectify API key | ||
""" | ||
api_endpoint = '/v2/profiles/' | ||
asset_token = get_root_asset_token(assets, asset_name) | ||
if not asset_token: | ||
return # If asset_token is not found, skip | ||
payload = json.dumps({'asset_token': asset_token, | ||
'endpoint': asset_name}) | ||
r = requests.post(url=f'{API_BASE_URL}{api_endpoint}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}, | ||
data=payload) | ||
print(f'Added scan profile for {asset_name} with code {r.status_code}: {r.reason}') | ||
|
||
|
||
def get_root_asset_token(assets: dict, asset_name: str) -> str: | ||
"""Look up the asset token associated with the root asset given a domain in Detectify | ||
:param assets: A dictionary of assets and tokens | ||
:param asset_name: The name of the asset to look up the root asset token for | ||
:return: A root asset token | ||
""" | ||
# For each named root asset, check to see if it is a substring of the provided asset name, and return the first | ||
try: | ||
return assets[[root_asset for root_asset in assets if root_asset in asset_name][0]] | ||
except IndexError: | ||
print(f'{asset_name} is not associated with any assets. Skipping.') | ||
|
||
|
||
def get_assets(key: str) -> dict: | ||
"""Get the full list of apex domains and subdomains from Detectify for later filtering | ||
:param key: A valid Detectify API key | ||
:return: A dictionary of assets and tokens | ||
""" | ||
print('Querying assets. . .') | ||
all_assets = {} | ||
marker = '' | ||
has_more = True | ||
while has_more: | ||
api_endpoint = f'/v2/assets/' | ||
r = requests.get(url=f'{API_BASE_URL}{api_endpoint}?marker={marker}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}) | ||
for asset in r.json()['assets']: | ||
all_assets[asset['name']] = asset['token'] | ||
try: | ||
marker = r.json()['next_marker'] | ||
except KeyError: # next_marker may not exist when has_more is False | ||
pass | ||
has_more = r.json()['has_more'] | ||
return all_assets | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description='Create Application Scan profiles in Detectify') | ||
parser.add_argument('key', type=str, help='a valid Detectify API key') | ||
parser.add_argument('-d', '--domain', type=str, nargs='*', | ||
help='one or more domains for Application Scan') | ||
parser.add_argument('-f', '--file', type=str, | ||
help='a file containing a list of domains') | ||
args = parser.parse_args() | ||
if not (args.domain or args.file): | ||
parser.error('No domains specified. Use at least one of flag -d or -f.') | ||
assets = get_assets(args.key) # Must get full list of assets to convert names to tokens | ||
print(f'Retrieved {len(assets)} assets') | ||
|
||
if args.domain: | ||
for domain in args.domain: | ||
create_scan_profile(assets, domain, args.key) | ||
|
||
if args.file: | ||
with open(args.domain_file) as domains_to_delete: | ||
for domain in domains_to_delete.read().splitlines(): | ||
create_scan_profile(assets, domain, args.key) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
"""remove_all_scan_profile_schedules.py: trigger scans on all Application Scan profiles in a given Detectify team | ||
This script triggers IMMEDIATE Application Scans. Please ensure you have permission to start scans before running this | ||
script! | ||
The API key permissions required by this script are the following: | ||
- Allow listing scan profiles | ||
- Allow deleting scan schedule | ||
Usage: remove_all_scan_profile_schedules.py [-h] key | ||
""" | ||
|
||
import argparse | ||
import requests | ||
|
||
API_BASE_URL = 'https://api.detectify.com/rest' | ||
|
||
|
||
def remove_scan_profile_schedule(profile: dict, key: str) -> None: | ||
"""Remove the configured schedule on a given Application Scan profile | ||
:param profile: A dictionary containing the necessary identifiers for an Application Scan profile | ||
:param key: A valid Detectify API key | ||
""" | ||
api_endpoint = f'/v2/scanschedules/{profile["token"]}/' | ||
r = requests.delete(url=f'{API_BASE_URL}{api_endpoint}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}) | ||
print(f'Removed scan schedule from scan profile {profile["name"]} with code {r.status_code}: {r.reason}') | ||
|
||
|
||
def get_scan_profiles(key: str) -> list: | ||
"""Get the full set of scan profiles from a given Detectify team | ||
:param key: A valid Detectify API key | ||
:return: A list of dictionaries containing identifiers for all Application Scan profiles | ||
""" | ||
print('Querying list of scan profiles...') | ||
api_endpoint = f'/v2/profiles/' | ||
r = requests.get(url=f'{API_BASE_URL}{api_endpoint}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}) | ||
return r.json() | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description='Run all application scan profiles') | ||
parser.add_argument('key', type=str, help='a valid Detectify API key') | ||
args = parser.parse_args() | ||
|
||
scan_profiles = get_scan_profiles(args.key) | ||
|
||
for profile in scan_profiles: | ||
remove_scan_profile_schedule(profile, args.key) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
"""run_all_application_scans.py: trigger scans on all Application Scan profiles in a given Detectify team | ||
This script triggers IMMEDIATE Application Scans. Please ensure you have permission to start scans before running this | ||
script! | ||
The API key permissions required by this script are the following: | ||
- Allow listing scan profiles | ||
- Allow starting scan | ||
Usage: run_all_application_scans.py [-h] key | ||
""" | ||
|
||
import argparse | ||
import requests | ||
|
||
API_BASE_URL = 'https://api.detectify.com/rest' | ||
|
||
|
||
def start_application_scan(profile: dict, key: str) -> None: | ||
"""Trigger an immediate scan on a given Application Scan profile | ||
:param profile: A dictionary containing the necessary identifiers for an Application Scan profile | ||
:param key: A valid Detectify API key | ||
""" | ||
api_endpoint = f'/v2/scans/{profile["token"]}/' | ||
r = requests.post(url=f'{API_BASE_URL}{api_endpoint}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}) | ||
print(f'Starting scan on {profile["endpoint"]} with code {r.status_code}: {r.reason}') | ||
|
||
|
||
def get_scan_profiles(key: str) -> list: | ||
"""Get the full set of scan profiles from a given Detectify team | ||
:param key: A valid Detectify API key | ||
:return: A list of dictionaries containing identifiers for all Application Scan profiles | ||
""" | ||
print('Querying list of scan profiles...') | ||
api_endpoint = f'/v2/profiles/' | ||
r = requests.get(url=f'{API_BASE_URL}{api_endpoint}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}) | ||
return r.json() | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description='Run all application scan profiles') | ||
parser.add_argument('key', type=str, help='a valid Detectify API key') | ||
args = parser.parse_args() | ||
|
||
scan_profiles = get_scan_profiles(args.key) | ||
|
||
for profile in scan_profiles: | ||
start_application_scan(profile, args.key) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
"""add_asset.py: add assets to a given Detectify team | ||
The API key permissions required by this script are the following: | ||
- Allow creating domains | ||
- Allow uploading zone files | ||
Usage: add_assets.py [-h] [-d [DOMAIN [DOMAIN ...]]] [-f FILE] | ||
[-z [ZONEFILE [ZONEFILE ...]]] | ||
key | ||
""" | ||
|
||
import argparse | ||
import json | ||
import requests | ||
|
||
API_BASE_URL = 'https://api.detectify.com/rest' | ||
|
||
|
||
def add_asset(domain: str, key: str) -> None: | ||
"""Add an asset to Detectify | ||
:param domain: An FQDN to add to the Detectify asset inventory | ||
:param key: A valid Detectify API key | ||
""" | ||
api_endpoint = f'/v2/assets/' | ||
payload = json.dumps({'name': domain.strip()}) | ||
r = requests.post(url=f'{API_BASE_URL}{api_endpoint}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}, | ||
data=payload) | ||
print(f'Added asset {domain.strip()} with code {r.status_code}: {r.reason}') | ||
|
||
|
||
def upload_zone_file(zone_file: str, key: str) -> None: | ||
"""Upload a zone file to populate domain information | ||
:param zone_file: A zone file including a specified $ORIGIN | ||
:param key: A valid Detectify API key | ||
""" | ||
api_endpoint = f'/v2/zone/file/' | ||
r = requests.post(url=f'{API_BASE_URL}{api_endpoint}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}, | ||
data=open(zone_file, 'rb')) | ||
print(f'Uploaded zone file {zone_file} with code {r.status_code}: {r.reason}') | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description='Add assets to Detectify') | ||
parser.add_argument('key', type=str, help='a valid Detectify API key') | ||
parser.add_argument('-d', '--domain', type=str, nargs='*', | ||
help='one or more domains to add to Detectify') | ||
parser.add_argument('-f', '--file', type=str, | ||
help='a file containing a list of apex domains') | ||
parser.add_argument('-z', '--zonefile', type=str, nargs='*', | ||
help='one or more zone files including a specified $ORIGIN') | ||
args = parser.parse_args() | ||
if not (args.domain or args.file or args.zonefile): | ||
parser.error('No domains specified. Use at least one of flag -d, -f, or -z.') | ||
|
||
if args.domain: | ||
for domain in args.domain: | ||
add_asset(domain, args.key) | ||
|
||
if args.file: | ||
with open(args.file, 'r') as infile: | ||
for domain in infile.read().splitlines(): | ||
add_asset(domain, args.key) | ||
|
||
if args.zonefile: | ||
for zone_file in args.zonefile: | ||
upload_zone_file(zone_file, args.key) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
"""delete_assets.py: delete specified root assets from a given Detectify team | ||
The API key permissions required by this script are the following: | ||
- Allow deleting domains | ||
- Allow listing domains | ||
Usage: delete_assets.py [-h] [-d [DOMAIN [DOMAIN ...]]] [-f FILE] key | ||
""" | ||
|
||
import argparse | ||
import requests | ||
|
||
API_BASE_URL = 'https://api.detectify.com/rest' | ||
|
||
|
||
def delete_asset(asset_token: str, asset_name: str, key: str) -> None: | ||
"""Delete a given Detectify asset | ||
:param asset_token: The internal UUID used to identify individual assets | ||
:param asset_name: The name of the asset associated with the given token | ||
:param key: A valid Detectify API key | ||
""" | ||
try: | ||
api_endpoint = f'/v2/assets/{asset_token}/' | ||
r = requests.delete(url=f'{API_BASE_URL}{api_endpoint}', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}) | ||
print(f'deleted asset {asset_name} with code {r.status_code}: {r.reason}') | ||
except KeyError: | ||
print(f'Asset {asset_name} not found, skipping') | ||
pass | ||
|
||
|
||
def get_assets(key: str) -> dict: | ||
"""Get the full list of apex domains and subdomains from Detectify for later filtering | ||
:param key: A valid Detectify API key | ||
:return: A dictionary of assets and tokens | ||
""" | ||
print('Querying assets. . .') | ||
all_assets = {} | ||
marker = '' | ||
has_more = True | ||
while has_more: | ||
api_endpoint = f'/v2/assets/' | ||
r = requests.get(url=f'{API_BASE_URL}{api_endpoint}?marker={marker}&include_subdomains=true', | ||
headers={'X-Detectify-Key': key, | ||
'content-type': 'application/json'}) | ||
for asset in r.json()['assets']: | ||
all_assets[asset['name']] = asset['token'] | ||
try: | ||
marker = r.json()['next_marker'] | ||
except KeyError: # next_marker may not exist when has_more is False | ||
pass | ||
has_more = r.json()['has_more'] | ||
return all_assets | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description='Delete assets from Detectify') | ||
parser.add_argument('key', type=str, help='a valid Detectify API key') | ||
parser.add_argument('-d', '--domain', type=str, nargs='*', | ||
help='one or more domains to add to Detectify') | ||
parser.add_argument('-f', '--file', type=str, | ||
help='a file containing a list of apex domains') | ||
args = parser.parse_args() | ||
if not (args.domain or args.file): | ||
parser.error('No domains specified. Use at least one of flag -d or -f.') | ||
assets = get_assets(args.key) # Must get full list of assets to convert names to tokens | ||
print(f'Retrieved {len(assets)} assets') | ||
|
||
if args.domain: | ||
for domain in args.domain: | ||
delete_asset(assets[domain], domain, args.key) | ||
|
||
if args.file: | ||
with open(args.domain_file) as domains_to_delete: | ||
for domain in domains_to_delete.read().splitlines(): | ||
delete_asset(assets[domain], domain, args.key) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Oops, something went wrong.