Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
detectify-zack authored Jun 25, 2024
1 parent 1d6b212 commit d0920c4
Show file tree
Hide file tree
Showing 8 changed files with 629 additions and 0 deletions.
103 changes: 103 additions & 0 deletions Application Scanning/add_scan_profiles.py
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()
58 changes: 58 additions & 0 deletions Application Scanning/remove_all_scan_profile_schedules.py
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()
58 changes: 58 additions & 0 deletions Application Scanning/run_all_application_scans.py
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()
76 changes: 76 additions & 0 deletions Asset Management/add_assets.py
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()
83 changes: 83 additions & 0 deletions Asset Management/delete_assets.py
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()
Loading

0 comments on commit d0920c4

Please sign in to comment.