Files
rengine/web/api/views.py
T

2924 lines
88 KiB
Python

import logging
import re
import socket
from ipaddress import IPv4Network
import requests
import validators
from dashboard.models import *
from django.db.models import CharField, Count, F, Q, Value
from django.shortcuts import get_object_or_404
from django.utils import timezone
from packaging import version
from django.template.defaultfilters import slugify
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.status import HTTP_400_BAD_REQUEST, HTTP_204_NO_CONTENT
from rest_framework.decorators import action
from recon_note.models import *
from reNgine.celery import app
from reNgine.common_func import *
from reNgine.definitions import ABORTED_TASK
from reNgine.tasks import *
from reNgine.llm import *
from reNgine.utilities import is_safe_path
from scanEngine.models import *
from startScan.models import *
from startScan.models import EndPoint
from targetApp.models import *
from .serializers import *
logger = logging.getLogger(__name__)
class InAppNotificationManagerViewSet(viewsets.ModelViewSet):
"""
This class manages the notification model, provided CRUD operation on notif model
such as read notif, clear all, fetch all notifications etc
"""
serializer_class = InAppNotificationSerializer
pagination_class = None
def get_queryset(self):
# we will see later if user based notif is needed
# return InAppNotification.objects.filter(user=self.request.user)
project_slug = self.request.query_params.get('project_slug')
queryset = InAppNotification.objects.all()
if project_slug:
queryset = queryset.filter(
Q(project__slug=project_slug) | Q(notification_type='system')
)
return queryset.order_by('-created_at')
@action(detail=False, methods=['post'])
def mark_all_read(self, request):
# marks all notification read
project_slug = self.request.query_params.get('project_slug')
queryset = self.get_queryset()
if project_slug:
queryset = queryset.filter(
Q(project__slug=project_slug) | Q(notification_type='system')
)
queryset.update(is_read=True)
return Response(status=HTTP_204_NO_CONTENT)
@action(detail=True, methods=['post'])
def mark_read(self, request, pk=None):
# mark individual notification read when cliked
notification = self.get_object()
notification.is_read = True
notification.save()
return Response(status=HTTP_204_NO_CONTENT)
@action(detail=False, methods=['get'])
def unread_count(self, request):
# this fetches the count for unread notif mainly for the badge
project_slug = self.request.query_params.get('project_slug')
queryset = self.get_queryset()
if project_slug:
queryset = queryset.filter(
Q(project__slug=project_slug) | Q(notification_type='system')
)
count = queryset.filter(is_read=False).count()
return Response({'count': count})
@action(detail=False, methods=['post'])
def clear_all(self, request):
# when clicked on the clear button this must be called to clear all notif
project_slug = self.request.query_params.get('project_slug')
queryset = self.get_queryset()
if project_slug:
queryset = queryset.filter(
Q(project__slug=project_slug) | Q(notification_type='system')
)
queryset.delete()
return Response(status=HTTP_204_NO_CONTENT)
class OllamaManager(APIView):
def get(self, request):
"""
API to download Ollama Models
sends a POST request to download the model
"""
req = self.request
model_name = req.query_params.get('model')
response = {
'status': False
}
try:
pull_model_api = f'{OLLAMA_INSTANCE}/api/pull'
_response = requests.post(
pull_model_api,
json={
'name': model_name,
'stream': False
}
).json()
if _response.get('error'):
response['status'] = False
response['error'] = _response.get('error')
else:
response['status'] = True
except Exception as e:
response['error'] = str(e)
return Response(response)
def delete(self, request):
req = self.request
model_name = req.query_params.get('model')
delete_model_api = f'{OLLAMA_INSTANCE}/api/delete'
response = {
'status': False
}
try:
_response = requests.delete(
delete_model_api,
json={
'name': model_name
}
).json()
if _response.get('error'):
response['status'] = False
response['error'] = _response.get('error')
else:
response['status'] = True
except Exception as e:
response['error'] = str(e)
return Response(response)
def put(self, request):
req = self.request
model_name = req.query_params.get('model')
# check if model_name is in DEFAULT_GPT_MODELS
response = {
'status': False
}
use_ollama = True
if any(model['name'] == model_name for model in DEFAULT_GPT_MODELS):
use_ollama = False
try:
OllamaSettings.objects.update_or_create(
defaults={
'selected_model': model_name,
'use_ollama': use_ollama
},
id=1
)
response['status'] = True
except Exception as e:
response['error'] = str(e)
return Response(response)
class GPTAttackSuggestion(APIView):
def get(self, request):
req = self.request
subdomain_id = req.query_params.get('subdomain_id')
if not subdomain_id:
return Response({
'status': False,
'error': 'Missing GET param Subdomain `subdomain_id`'
})
try:
subdomain = Subdomain.objects.get(id=subdomain_id)
except Exception as e:
return Response({
'status': False,
'error': 'Subdomain not found with id ' + subdomain_id
})
if subdomain.attack_surface:
return Response({
'status': True,
'subdomain_name': subdomain.name,
'description': subdomain.attack_surface
})
ip_addrs = subdomain.ip_addresses.all()
open_ports_str = ''
for ip in ip_addrs:
ports = ip.ports.all()
for port in ports:
open_ports_str += f'{port.number}/{port.service_name}, '
tech_used = ''
for tech in subdomain.technologies.all():
tech_used += f'{tech.name}, '
llm_input = f'''
Subdomain Name: {subdomain.name}
Subdomain Page Title: {subdomain.page_title}
Open Ports: {open_ports_str}
HTTP Status: {subdomain.http_status}
Technologies Used: {tech_used}
Content type: {subdomain.content_type}
Web Server: {subdomain.webserver}
Page Content Length: {subdomain.content_length}
'''
llm_input = re.sub(r'\t', '', llm_input)
gpt = LLMAttackSuggestionGenerator(logger)
response = gpt.get_attack_suggestion(llm_input)
response['subdomain_name'] = subdomain.name
if response.get('status'):
subdomain.attack_surface = response.get('description')
subdomain.save()
return Response(response)
class LLMVulnerabilityReportGenerator(APIView):
def get(self, request):
req = self.request
vulnerability_id = req.query_params.get('id')
if not vulnerability_id:
return Response({
'status': False,
'error': 'Missing GET param Vulnerability `id`'
})
task = llm_vulnerability_description.apply_async(args=(vulnerability_id,))
response = task.wait()
return Response(response)
class CreateProjectApi(APIView):
def get(self, request):
req = self.request
project_name = req.query_params.get('name')
slug = slugify(project_name)
insert_date = timezone.now()
try:
project = Project.objects.create(
name=project_name,
slug=slug,
insert_date =insert_date
)
response = {
'status': True,
'project_name': project_name
}
return Response(response)
except Exception as e:
response = {
'status': False,
'error': str(e)
}
return Response(response, status=HTTP_400_BAD_REQUEST)
class QueryInterestingSubdomains(APIView):
def get(self, request):
req = self.request
scan_id = req.query_params.get('scan_id')
domain_id = req.query_params.get('target_id')
if scan_id:
queryset = get_interesting_subdomains(scan_history=scan_id)
elif domain_id:
queryset = get_interesting_subdomains(domain_id=domain_id)
else:
queryset = get_interesting_subdomains()
queryset = queryset.distinct('name')
return Response(InterestingSubdomainSerializer(queryset, many=True).data)
class ListTargetsDatatableViewSet(viewsets.ModelViewSet):
queryset = Domain.objects.all()
serializer_class = DomainSerializer
def get_queryset(self):
slug = self.request.GET.get('slug', None)
if slug:
self.queryset = self.queryset.filter(project__slug=slug)
return self.queryset
def filter_queryset(self, qs):
qs = self.queryset.filter()
search_value = self.request.GET.get(u'search[value]', None)
_order_col = self.request.GET.get(u'order[0][column]', None)
_order_direction = self.request.GET.get(u'order[0][dir]', None)
if search_value or _order_col or _order_direction:
order_col = 'id'
if _order_col == '2':
order_col = 'name'
elif _order_col == '4':
order_col = 'insert_date'
elif _order_col == '5':
order_col = 'start_scan_date'
if _order_direction == 'desc':
return qs.order_by(F('start_scan_date').desc(nulls_last=True))
return qs.order_by(F('start_scan_date').asc(nulls_last=True))
if _order_direction == 'desc':
order_col = f'-{order_col}'
qs = self.queryset.filter(
Q(name__icontains=search_value) |
Q(description__icontains=search_value) |
Q(domains__name__icontains=search_value)
)
return qs.order_by(order_col)
return qs.order_by('-id')
class WafDetector(APIView):
def get(self, request):
req = self.request
url= req.query_params.get('url')
response = {}
response['status'] = False
# validate url as a first step to avoid command injection
if not (validators.url(url) or validators.domain(url)):
response['message'] = 'Invalid Domain/URL provided!'
return Response(response)
wafw00f_command = f'wafw00f {url}'
_, output = run_command(wafw00f_command, remove_ansi_sequence=True)
regex = r"behind (.*?) WAF"
group = re.search(regex, output)
if group:
response['status'] = True
response['results'] = group.group(1)
else:
response['message'] = 'Could not detect any WAF!'
return Response(response)
class SearchHistoryView(APIView):
def get(self, request):
req = self.request
response = {}
response['status'] = False
scan_history = SearchHistory.objects.all().order_by('-id')[:5]
if scan_history:
response['status'] = True
response['results'] = SearchHistorySerializer(scan_history, many=True).data
return Response(response)
class UniversalSearch(APIView):
def get(self, request):
req = self.request
query = req.query_params.get('query')
response = {}
response['status'] = False
if not query:
response['message'] = 'No query parameter provided!'
return Response(response)
response['results'] = {}
# search history to be saved
SearchHistory.objects.get_or_create(
query=query
)
# lookup query in subdomain
subdomain = Subdomain.objects.filter(
Q(name__icontains=query) |
Q(cname__icontains=query) |
Q(page_title__icontains=query) |
Q(http_url__icontains=query)
).distinct('name')
subdomain_data = SubdomainSerializer(subdomain, many=True).data
response['results']['subdomains'] = subdomain_data
endpoint = EndPoint.objects.filter(
Q(http_url__icontains=query) |
Q(page_title__icontains=query)
).distinct('http_url')
endpoint_data = EndpointSerializer(endpoint, many=True).data
response['results']['endpoints'] = endpoint_data
vulnerability = Vulnerability.objects.filter(
Q(http_url__icontains=query) |
Q(name__icontains=query) |
Q(description__icontains=query)
).distinct()
vulnerability_data = VulnerabilitySerializer(vulnerability, many=True).data
response['results']['vulnerabilities'] = vulnerability_data
response['results']['others'] = {}
if subdomain_data or endpoint_data or vulnerability_data:
response['status'] = True
return Response(response)
class FetchMostCommonVulnerability(APIView):
def post(self, request):
req = self.request
data = req.data
try:
limit = data.get('limit', 20)
project_slug = data.get('slug')
scan_history_id = data.get('scan_history_id')
target_id = data.get('target_id')
is_ignore_info = data.get('ignore_info', False)
response = {}
response['status'] = False
if project_slug:
project = Project.objects.get(slug=project_slug)
vulnerabilities = Vulnerability.objects.filter(target_domain__project=project)
else:
vulnerabilities = Vulnerability.objects.all()
if scan_history_id:
vuln_query = (
vulnerabilities
.filter(scan_history__id=scan_history_id)
.values("name", "severity")
)
if is_ignore_info:
most_common_vulnerabilities = (
vuln_query
.exclude(severity=0)
.annotate(count=Count('name'))
.order_by("-count")[:limit]
)
else:
most_common_vulnerabilities = (
vuln_query
.annotate(count=Count('name'))
.order_by("-count")[:limit]
)
elif target_id:
vuln_query = vulnerabilities.filter(target_domain__id=target_id).values("name", "severity")
if is_ignore_info:
most_common_vulnerabilities = (
vuln_query
.exclude(severity=0)
.annotate(count=Count('name'))
.order_by("-count")[:limit]
)
else:
most_common_vulnerabilities = (
vuln_query
.annotate(count=Count('name'))
.order_by("-count")[:limit]
)
else:
vuln_query = vulnerabilities.values("name", "severity")
if is_ignore_info:
most_common_vulnerabilities = (
vuln_query.exclude(severity=0)
.annotate(count=Count('name'))
.order_by("-count")[:limit]
)
else:
most_common_vulnerabilities = (
vuln_query.annotate(count=Count('name'))
.order_by("-count")[:limit]
)
most_common_vulnerabilities = [vuln for vuln in most_common_vulnerabilities]
if most_common_vulnerabilities:
response['status'] = True
response['result'] = most_common_vulnerabilities
except Exception as e:
print(str(e))
response = {}
return Response(response)
class FetchMostVulnerable(APIView):
def post(self, request):
req = self.request
data = req.data
project_slug = data.get('slug')
scan_history_id = data.get('scan_history_id')
target_id = data.get('target_id')
limit = data.get('limit', 20)
is_ignore_info = data.get('ignore_info', False)
response = {}
response['status'] = False
if project_slug:
project = Project.objects.get(slug=project_slug)
subdomains = Subdomain.objects.filter(target_domain__project=project)
domains = Domain.objects.filter(project=project)
else:
subdomains = Subdomain.objects.all()
domains = Domain.objects.all()
if scan_history_id:
subdomain_query = subdomains.filter(scan_history__id=scan_history_id)
if is_ignore_info:
most_vulnerable_subdomains = (
subdomain_query
.annotate(
vuln_count=Count('vulnerability__name', filter=~Q(vulnerability__severity=0))
)
.order_by('-vuln_count')
.exclude(vuln_count=0)[:limit]
)
else:
most_vulnerable_subdomains = (
subdomain_query
.annotate(vuln_count=Count('vulnerability__name'))
.order_by('-vuln_count')
.exclude(vuln_count=0)[:limit]
)
if most_vulnerable_subdomains:
response['status'] = True
response['result'] = (
SubdomainSerializer(
most_vulnerable_subdomains,
many=True)
.data
)
elif target_id:
subdomain_query = subdomains.filter(target_domain__id=target_id)
if is_ignore_info:
most_vulnerable_subdomains = (
subdomain_query
.annotate(vuln_count=Count('vulnerability__name', filter=~Q(vulnerability__severity=0)))
.order_by('-vuln_count')
.exclude(vuln_count=0)[:limit]
)
else:
most_vulnerable_subdomains = (
subdomain_query
.annotate(vuln_count=Count('vulnerability__name'))
.order_by('-vuln_count')
.exclude(vuln_count=0)[:limit]
)
if most_vulnerable_subdomains:
response['status'] = True
response['result'] = (
SubdomainSerializer(
most_vulnerable_subdomains,
many=True)
.data
)
else:
if is_ignore_info:
most_vulnerable_targets = (
domains
.annotate(vuln_count=Count('subdomain__vulnerability__name', filter=~Q(subdomain__vulnerability__severity=0)))
.order_by('-vuln_count')
.exclude(vuln_count=0)[:limit]
)
else:
most_vulnerable_targets = (
domains
.annotate(vuln_count=Count('subdomain__vulnerability__name'))
.order_by('-vuln_count')
.exclude(vuln_count=0)[:limit]
)
if most_vulnerable_targets:
response['status'] = True
response['result'] = (
DomainSerializer(
most_vulnerable_targets,
many=True)
.data
)
return Response(response)
class CVEDetails(APIView):
def get(self, request):
req = self.request
cve_id = req.query_params.get('cve_id')
if not cve_id:
return Response({'status': False, 'message': 'CVE ID not provided'})
response = requests.get('https://cve.circl.lu/api/cve/' + cve_id)
if response.status_code != 200:
return Response({'status': False, 'message': 'Unknown Error Occured!'})
if not response.json():
return Response({'status': False, 'message': 'CVE ID does not exists.'})
return Response({'status': True, 'result': response.json()})
class AddReconNote(APIView):
def post(self, request):
req = self.request
data = req.data
subdomain_id = data.get('subdomain_id')
title = data.get('title')
description = data.get('description')
project = data.get('project')
try:
project = Project.objects.get(slug=project)
note = TodoNote()
note.title = title
note.description = description
# get scan history for subdomain_id
if subdomain_id:
subdomain = Subdomain.objects.get(id=subdomain_id)
note.subdomain = subdomain
# also get scan history
scan_history_id = subdomain.scan_history.id
scan_history = ScanHistory.objects.get(id=scan_history_id)
note.scan_history = scan_history
note.project = project
note.save()
response = {'status': True}
except Exception as e:
response = {'status': False, 'message': str(e)}
return Response(response)
class ToggleSubdomainImportantStatus(APIView):
def post(self, request):
req = self.request
data = req.data
subdomain_id = data.get('subdomain_id')
response = {'status': False, 'message': 'No subdomain_id provided'}
name = Subdomain.objects.get(id=subdomain_id)
name.is_important = not name.is_important
name.save()
response = {'status': True}
return Response(response)
class AddTarget(APIView):
def post(self, request):
req = self.request
data = req.data
h1_team_handle = data.get('h1_team_handle')
description = data.get('description')
domain_name = data.get('domain_name')
organization_name = data.get('organization')
slug = data.get('slug')
# Validate domain name
if not validators.domain(domain_name):
return Response({'status': False, 'message': 'Invalid domain or IP'})
project = Project.objects.get(slug=slug)
# Create domain object in DB
domain, _ = Domain.objects.get_or_create(name=domain_name)
domain.project = project
domain.h1_team_handle = h1_team_handle
domain.description = description
if not domain.insert_date:
domain.insert_date = timezone.now()
domain.save()
# Create org object in DB
if organization_name:
organization_obj = None
organization_query = Organization.objects.filter(name=organization_name)
if organization_query.exists():
organization_obj = organization_query[0]
else:
organization_obj = Organization.objects.create(
name=organization_name,
project=project,
insert_date=timezone.now())
organization_obj.domains.add(domain)
return Response({
'status': True,
'message': 'Domain successfully added as target !',
'domain_name': domain_name,
'domain_id': domain.id
})
class FetchSubscanResults(APIView):
def get(self, request):
req = self.request
# data = req.data
subscan_id = req.query_params.get('subscan_id')
subscan = SubScan.objects.filter(id=subscan_id)
if not subscan.exists():
return Response({
'status': False,
'error': f'Subscan {subscan_id} does not exist'
})
subscan_data = SubScanResultSerializer(subscan.first(), many=False).data
task_name = subscan_data['type']
subscan_results = []
if task_name == 'port_scan':
ips_in_subscan = IpAddress.objects.filter(ip_subscan_ids__in=subscan)
subscan_results = IpSerializer(ips_in_subscan, many=True).data
elif task_name == 'vulnerability_scan':
vulns_in_subscan = Vulnerability.objects.filter(vuln_subscan_ids__in=subscan)
subscan_results = VulnerabilitySerializer(vulns_in_subscan, many=True).data
elif task_name == 'fetch_url':
endpoints_in_subscan = EndPoint.objects.filter(endpoint_subscan_ids__in=subscan)
subscan_results = EndpointSerializer(endpoints_in_subscan, many=True).data
elif task_name == 'dir_file_fuzz':
dirs_in_subscan = DirectoryScan.objects.filter(dir_subscan_ids__in=subscan)
subscan_results = DirectoryScanSerializer(dirs_in_subscan, many=True).data
elif task_name == 'subdomain_discovery':
subdomains_in_subscan = Subdomain.objects.filter(subdomain_subscan_ids__in=subscan)
subscan_results = SubdomainSerializer(subdomains_in_subscan, many=True).data
elif task_name == 'screenshot':
subdomains_in_subscan = Subdomain.objects.filter(subdomain_subscan_ids__in=subscan, screenshot_path__isnull=False)
subscan_results = SubdomainSerializer(subdomains_in_subscan, many=True).data
logger.info(subscan_data)
logger.info(subscan_results)
return Response({'subscan': subscan_data, 'result': subscan_results})
class ListSubScans(APIView):
def post(self, request):
req = self.request
data = req.data
subdomain_id = data.get('subdomain_id', None)
scan_history = data.get('scan_history_id', None)
domain_id = data.get('domain_id', None)
response = {}
response['status'] = False
if subdomain_id:
subscans = (
SubScan.objects
.filter(subdomain__id=subdomain_id)
.order_by('-stop_scan_date')
)
results = SubScanSerializer(subscans, many=True).data
if subscans:
response['status'] = True
response['results'] = results
elif scan_history:
subscans = (
SubScan.objects
.filter(scan_history__id=scan_history)
.order_by('-stop_scan_date')
)
results = SubScanSerializer(subscans, many=True).data
if subscans:
response['status'] = True
response['results'] = results
elif domain_id:
scan_history = ScanHistory.objects.filter(domain__id=domain_id)
subscans = (
SubScan.objects
.filter(scan_history__in=scan_history)
.order_by('-stop_scan_date')
)
results = SubScanSerializer(subscans, many=True).data
if subscans:
response['status'] = True
response['results'] = results
return Response(response)
class DeleteMultipleRows(APIView):
def post(self, request):
req = self.request
data = req.data
try:
if data['type'] == 'subscan':
for row in data['rows']:
SubScan.objects.get(id=row).delete()
response = True
except Exception as e:
response = False
return Response({'status': response})
class StopScan(APIView):
def post(self, request):
req = self.request
data = req.data
scan_ids = data.get('scan_ids', [])
subscan_ids = data.get('subscan_ids', [])
scan_ids = [int(id) for id in scan_ids]
subscan_ids = [int(id) for id in subscan_ids]
response = {'status': False}
def abort_scan(scan):
response = {}
logger.info(f'Aborting scan History')
try:
logger.info(f"Setting scan {scan} status to ABORTED_TASK")
task_ids = scan.celery_ids
scan.scan_status = ABORTED_TASK
scan.stop_scan_date = timezone.now()
scan.aborted_by = request.user
scan.save()
for task_id in task_ids:
app.control.revoke(task_id, terminate=True, signal='SIGKILL')
tasks = (
ScanActivity.objects
.filter(scan_of=scan)
.filter(status=RUNNING_TASK)
.order_by('-pk')
)
for task in tasks:
task.status = ABORTED_TASK
task.time = timezone.now()
task.save()
create_scan_activity(
scan.id,
"Scan aborted",
ABORTED_TASK
)
response['status'] = True
except Exception as e:
logger.error(e)
response = {'status': False, 'message': str(e)}
return response
def abort_subscan(subscan):
response = {}
logger.info(f'Aborting subscan')
try:
logger.info(f"Setting scan {subscan} status to ABORTED_TASK")
task_ids = subscan.celery_ids
for task_id in task_ids:
app.control.revoke(task_id, terminate=True, signal='SIGKILL')
subscan.status = ABORTED_TASK
subscan.stop_scan_date = timezone.now()
subscan.save()
create_scan_activity(
subscan.scan_history.id,
f'Subscan aborted',
ABORTED_TASK
)
response['status'] = True
except Exception as e:
logger.error(e)
response = {'status': False, 'message': str(e)}
return response
for scan_id in scan_ids:
try:
scan = ScanHistory.objects.get(id=scan_id)
# if scan is already successful or aborted then do nothing
if scan.scan_status == SUCCESS_TASK or scan.scan_status == ABORTED_TASK:
continue
response = abort_scan(scan)
except Exception as e:
logger.error(e)
response = {'status': False, 'message': str(e)}
for subscan_id in subscan_ids:
try:
subscan = SubScan.objects.get(id=subscan_id)
if subscan.scan_status == SUCCESS_TASK or subscan.scan_status == ABORTED_TASK:
continue
response = abort_subscan(subscan)
except Exception as e:
logger.error(e)
response = {'status': False, 'message': str(e)}
return Response(response)
class InitiateSubTask(APIView):
def post(self, request):
req = self.request
data = req.data
engine_id = data.get('engine_id')
scan_types = data['tasks']
for subdomain_id in data['subdomain_ids']:
logger.info(f'Running subscans {scan_types} on subdomain "{subdomain_id}" ...')
for stype in scan_types:
ctx = {
'scan_history_id': None,
'subdomain_id': subdomain_id,
'scan_type': stype,
'engine_id': engine_id
}
initiate_subscan.apply_async(kwargs=ctx)
return Response({'status': True})
class DeleteSubdomain(APIView):
def post(self, request):
req = self.request
for id in req.data['subdomain_ids']:
Subdomain.objects.get(id=id).delete()
return Response({'status': True})
class DeleteVulnerability(APIView):
def post(self, request):
req = self.request
for id in req.data['vulnerability_ids']:
Vulnerability.objects.get(id=id).delete()
return Response({'status': True})
class ListInterestingKeywords(APIView):
def get(self, request, format=None):
req = self.request
keywords = get_lookup_keywords()
return Response(keywords)
class RengineUpdateCheck(APIView):
def get(self, request):
req = self.request
github_api = \
'https://api.github.com/repos/yogeshojha/rengine/releases'
response = requests.get(github_api).json()
if 'message' in response:
return Response({'status': False, 'message': 'RateLimited'})
return_response = {}
# get current version_number
# remove quotes from current_version
current_version = RENGINE_CURRENT_VERSION
# for consistency remove v from both if exists
latest_version = re.search(r'v(\d+\.)?(\d+\.)?(\*|\d+)',
((response[0]['name'
])[1:] if response[0]['name'][0] == 'v'
else response[0]['name']))
latest_version = latest_version.group(0) if latest_version else None
if not latest_version:
latest_version = re.search(r'(\d+\.)?(\d+\.)?(\*|\d+)',
((response[0]['name'
])[1:] if response[0]['name'][0]
== 'v' else response[0]['name']))
if latest_version:
latest_version = latest_version.group(0)
return_response['status'] = True
return_response['latest_version'] = latest_version
return_response['current_version'] = current_version
is_version_update_available = version.parse(current_version) < version.parse(latest_version)
# if is_version_update_available then we should create inapp notification
create_inappnotification(
title='reNgine Update Available',
description=f'Update to version {latest_version} is available',
notification_type=SYSTEM_LEVEL_NOTIFICATION,
project_slug=None,
icon='mdi-update',
redirect_link='https://github.com/yogeshojha/rengine/releases',
open_in_new_tab=True
)
return_response['update_available'] = is_version_update_available
if is_version_update_available:
return_response['changelog'] = response[0]['body']
return Response(return_response)
class UninstallTool(APIView):
def get(self, request):
req = self.request
tool_id = req.query_params.get('tool_id')
tool_name = req.query_params.get('name')
if tool_id:
tool = InstalledExternalTool.objects.get(id=tool_id)
elif tool_name:
tool = InstalledExternalTool.objects.get(name=tool_name)
if tool.is_default:
return Response({'status': False, 'message': 'Default tools can not be uninstalled'})
# check install instructions, if it is installed using go, then remove from go bin path,
# else try to remove from github clone path
# getting tool name is tricky!
if 'go install' in tool.install_command:
tool_name = tool.install_command.split('/')[-1].split('@')[0]
uninstall_command = 'rm /go/bin/' + tool_name
elif 'git clone' in tool.install_command:
tool_name = tool.install_command[:-1] if tool.install_command[-1] == '/' else tool.install_command
tool_name = tool_name.split('/')[-1]
uninstall_command = 'rm -rf ' + tool.github_clone_path
else:
return Response({'status': False, 'message': 'Cannot uninstall tool!'})
run_command(uninstall_command)
run_command.apply_async(args=(uninstall_command,))
tool.delete()
return Response({'status': True, 'message': 'Uninstall Tool Success'})
class UpdateTool(APIView):
def get(self, request):
req = self.request
tool_id = req.query_params.get('tool_id')
tool_name = req.query_params.get('name')
if tool_id:
tool = InstalledExternalTool.objects.get(id=tool_id)
elif tool_name:
tool = InstalledExternalTool.objects.get(name=tool_name)
# if git clone was used for installation, then we must use git pull inside project directory,
# otherwise use the same command as given
update_command = tool.update_command.lower()
if not update_command:
return Response({'status': False, 'message': tool.name + 'has missing update command! Cannot update the tool.'})
elif update_command == 'git pull':
tool_name = tool.install_command[:-1] if tool.install_command[-1] == '/' else tool.install_command
tool_name = tool_name.split('/')[-1]
update_command = 'cd /usr/src/github/' + tool_name + ' && git pull && cd -'
try:
run_command(update_command, shell=True)
run_command.apply_async(args=[update_command], kwargs={'shell': True})
return Response({'status': True, 'message': tool.name + ' updated successfully.'})
except Exception as e:
logger.error(str(e))
return Response({'status': False, 'message': str(e)})
class GetExternalToolCurrentVersion(APIView):
def get(self, request):
req = self.request
# toolname is also the command
tool_id = req.query_params.get('tool_id')
tool_name = req.query_params.get('name')
# can supply either tool id or tool_name
tool = None
if tool_id:
if not InstalledExternalTool.objects.filter(id=tool_id).exists():
return Response({'status': False, 'message': 'Tool Not found'})
tool = InstalledExternalTool.objects.get(id=tool_id)
elif tool_name:
if not InstalledExternalTool.objects.filter(name=tool_name).exists():
return Response({'status': False, 'message': 'Tool Not found'})
tool = InstalledExternalTool.objects.get(name=tool_name)
if not tool.version_lookup_command:
return Response({'status': False, 'message': 'Version Lookup command not provided.'})
version_number = None
_, stdout = run_command(tool.version_lookup_command)
if tool.version_match_regex:
version_number = re.search(re.compile(tool.version_match_regex), str(stdout))
else:
version_match_regex = r'(?i:v)?(\d+(?:\.\d+){2,})'
version_number = re.search(version_match_regex, str(stdout))
if not version_number:
return Response({'status': False, 'message': 'Invalid version lookup command.'})
return Response({'status': True, 'version_number': version_number.group(0), 'tool_name': tool.name})
class GithubToolCheckGetLatestRelease(APIView):
def get(self, request):
req = self.request
tool_id = req.query_params.get('tool_id')
tool_name = req.query_params.get('name')
if not InstalledExternalTool.objects.filter(id=tool_id).exists():
return Response({'status': False, 'message': 'Tool Not found'})
if tool_id:
tool = InstalledExternalTool.objects.get(id=tool_id)
elif tool_name:
tool = InstalledExternalTool.objects.get(name=tool_name)
if not tool.github_url:
return Response({'status': False, 'message': 'Github URL is not provided, Cannot check updates'})
# if tool_github_url has https://github.com/ remove and also remove trailing /
tool_github_url = tool.github_url.replace('http://github.com/', '').replace('https://github.com/', '')
tool_github_url = remove_lead_and_trail_slash(tool_github_url)
github_api = f'https://api.github.com/repos/{tool_github_url}/releases'
response = requests.get(github_api).json()
# check if api rate limit exceeded
if 'message' in response and response['message'] == 'RateLimited':
return Response({'status': False, 'message': 'RateLimited'})
elif 'message' in response and response['message'] == 'Not Found':
return Response({'status': False, 'message': 'Not Found'})
elif not response:
return Response({'status': False, 'message': 'Not Found'})
# only send latest release
response = response[0]
api_response = {
'status': True,
'url': response['url'],
'id': response['id'],
'name': response['name'],
'changelog': response['body'],
}
return Response(api_response)
class ScanStatus(APIView):
def get(self, request):
req = self.request
slug = self.request.GET.get('project', None)
# main tasks
recently_completed_scans = (
ScanHistory.objects
.filter(domain__project__slug=slug)
.order_by('-start_scan_date')
.filter(Q(scan_status=0) | Q(scan_status=2) | Q(scan_status=3))[:10]
)
current_scans = (
ScanHistory.objects
.filter(domain__project__slug=slug)
.order_by('-start_scan_date')
.filter(scan_status=1)
)
pending_scans = (
ScanHistory.objects
.filter(domain__project__slug=slug)
.filter(scan_status=-1)
)
# subtasks
recently_completed_tasks = (
SubScan.objects
.filter(scan_history__domain__project__slug=slug)
.order_by('-start_scan_date')
.filter(Q(status=0) | Q(status=2) | Q(status=3))[:15]
)
current_tasks = (
SubScan.objects
.filter(scan_history__domain__project__slug=slug)
.order_by('-start_scan_date')
.filter(status=1)
)
pending_tasks = (
SubScan.objects
.filter(scan_history__domain__project__slug=slug)
.filter(status=-1)
)
response = {
'scans': {
'pending': ScanHistorySerializer(pending_scans, many=True).data,
'scanning': ScanHistorySerializer(current_scans, many=True).data,
'completed': ScanHistorySerializer(recently_completed_scans, many=True).data
},
'tasks': {
'pending': SubScanSerializer(pending_tasks, many=True).data,
'running': SubScanSerializer(current_tasks, many=True).data,
'completed': SubScanSerializer(recently_completed_tasks, many=True).data
}
}
return Response(response)
class Whois(APIView):
def get(self, request):
req = self.request
target = req.query_params.get('target')
if not target:
return Response({'status': False, 'message': 'Target IP/Domain required!'})
if not (validators.domain(target) or validators.ipv4(target) or validators.ipv6(target)):
print(f'Ip address or domain "{target}" did not pass validator.')
return Response({'status': False, 'message': 'Invalid domain or IP'})
is_force_update = req.query_params.get('is_reload')
is_force_update = True if is_force_update and 'true' == is_force_update.lower() else False
task = query_whois.apply_async(args=(target,is_force_update))
response = task.wait()
return Response(response)
class ReverseWhois(APIView):
def get(self, request):
req = self.request
lookup_keyword = req.query_params.get('lookup_keyword')
task = query_reverse_whois.apply_async(args=(lookup_keyword,))
response = task.wait()
return Response(response)
class DomainIPHistory(APIView):
def get(self, request):
req = self.request
domain = req.query_params.get('domain')
task = query_ip_history.apply_async(args=(domain,))
response = task.wait()
return Response(response)
class CMSDetector(APIView):
def get(self, request):
req = self.request
url = req.query_params.get('url')
#save_db = True if 'save_db' in req.query_params else False
response = {'status': False}
if not (validators.url(url) or validators.domain(url)):
response['message'] = 'Invalid Domain/URL provided!'
return Response(response)
try:
# response = get_cms_details(url)
response = {}
cms_detector_command = f'python3 /usr/src/github/CMSeeK/cmseek.py'
cms_detector_command += ' --random-agent --batch --follow-redirect'
cms_detector_command += f' -u {url}'
_, output = run_command(cms_detector_command, remove_ansi_sequence=True)
response['message'] = 'Could not detect CMS!'
parsed_url = urlparse(url)
domain_name = parsed_url.hostname
port = parsed_url.port
find_dir = domain_name
if port:
find_dir += '_{}'.format(port)
# look for result path in output
path_regex = r"Result: (\/usr\/src[^\"\s]*)"
match = re.search(path_regex, output)
if match:
cms_json_path = match.group(1)
if os.path.isfile(cms_json_path):
cms_file_content = json.loads(open(cms_json_path, 'r').read())
if not cms_file_content.get('cms_id'):
return response
response = {}
response = cms_file_content
response['status'] = True
try:
# remove results
cms_dir_path = os.path.dirname(cms_json_path)
shutil.rmtree(cms_dir_path)
except Exception as e:
logger.error(e)
return Response(response)
return Response(response)
except Exception as e:
response = {'status': False, 'message': str(e)}
return Response(response)
class IPToDomain(APIView):
def get(self, request):
req = self.request
ip_address = req.query_params.get('ip_address')
if not ip_address:
return Response({
'status': False,
'message': 'IP Address Required'
})
try:
logger.info(f'Resolving IP address {ip_address} ...')
resolved_ips = []
for ip in IPv4Network(ip_address, False):
domains = []
ips = []
try:
(domain, domains, ips) = socket.gethostbyaddr(str(ip))
except socket.herror:
logger.info(f'No PTR record for {ip_address}')
domain = str(ip)
if domain not in domains:
domains.append(domain)
resolved_ips.append({'ip': str(ip),'domain': domain, 'domains': domains, 'ips': ips})
response = {
'status': True,
'orig': ip_address,
'ip_address': resolved_ips,
}
except Exception as e:
logger.exception(e)
response = {
'status': False,
'ip_address': ip_address,
'message': f'Exception {e}'
}
finally:
return Response(response)
class VulnerabilityReport(APIView):
def get(self, request):
req = self.request
vulnerability_id = req.query_params.get('vulnerability_id')
return Response({"status": send_hackerone_report(vulnerability_id)})
class GetFileContents(APIView):
def get(self, request, format=None):
req = self.request
name = req.query_params.get('name')
response = {}
response['status'] = False
if 'nuclei_config' in req.query_params:
path = "/root/.config/nuclei/config.yaml"
if not os.path.exists(path):
run_command(f'touch {path}')
response['message'] = 'File Created!'
f = open(path, "r")
response['status'] = True
response['content'] = f.read()
return Response(response)
if 'subfinder_config' in req.query_params:
path = "/root/.config/subfinder/config.yaml"
if not os.path.exists(path):
run_command(f'touch {path}')
response['message'] = 'File Created!'
f = open(path, "r")
response['status'] = True
response['content'] = f.read()
return Response(response)
if 'naabu_config' in req.query_params:
path = "/root/.config/naabu/config.yaml"
if not os.path.exists(path):
run_command(f'touch {path}')
response['message'] = 'File Created!'
f = open(path, "r")
response['status'] = True
response['content'] = f.read()
return Response(response)
if 'theharvester_config' in req.query_params:
path = "/usr/src/github/theHarvester/api-keys.yaml"
if not os.path.exists(path):
run_command(f'touch {path}')
response['message'] = 'File Created!'
f = open(path, "r")
response['status'] = True
response['content'] = f.read()
return Response(response)
if 'amass_config' in req.query_params:
path = "/root/.config/amass.ini"
if not os.path.exists(path):
run_command(f'touch {path}')
response['message'] = 'File Created!'
f = open(path, "r")
response['status'] = True
response['content'] = f.read()
return Response(response)
if 'gf_pattern' in req.query_params:
basedir = '/root/.gf'
path = f'/root/.gf/{name}.json'
if is_safe_path(basedir, path) and os.path.exists(path):
content = open(path, "r").read()
response['status'] = True
response['content'] = content
else:
response['message'] = "Invalid path!"
response['status'] = False
return Response(response)
if 'nuclei_template' in req.query_params:
safe_dir = '/root/nuclei-templates'
path = f'/root/nuclei-templates/{name}'
if is_safe_path(safe_dir, path) and os.path.exists(path):
content = open(path.format(name), "r").read()
response['status'] = True
response['content'] = content
else:
response['message'] = 'Invalid Path!'
response['status'] = False
return Response(response)
response['message'] = 'Invalid Query Params'
return Response(response)
class ListTodoNotes(APIView):
def get(self, request, format=None):
req = self.request
notes = TodoNote.objects.all().order_by('-id')
scan_id = req.query_params.get('scan_id')
project = req.query_params.get('project')
if project:
notes = notes.filter(project__slug=project)
target_id = req.query_params.get('target_id')
todo_id = req.query_params.get('todo_id')
subdomain_id = req.query_params.get('subdomain_id')
if target_id:
notes = notes.filter(scan_history__in=ScanHistory.objects.filter(domain__id=target_id))
elif scan_id:
notes = notes.filter(scan_history__id=scan_id)
if todo_id:
notes = notes.filter(id=todo_id)
if subdomain_id:
notes = notes.filter(subdomain__id=subdomain_id)
notes = ReconNoteSerializer(notes, many=True)
return Response({'notes': notes.data})
class ListScanHistory(APIView):
def get(self, request, format=None):
req = self.request
scan_history = ScanHistory.objects.all().order_by('-start_scan_date')
project = req.query_params.get('project')
if project:
scan_history = scan_history.filter(domain__project__slug=project)
scan_history = ScanHistorySerializer(scan_history, many=True)
return Response(scan_history.data)
class ListEngines(APIView):
def get(self, request, format=None):
req = self.request
engines = EngineType.objects.order_by('engine_name').all()
engine_serializer = EngineSerializer(engines, many=True)
return Response({'engines': engine_serializer.data})
class ListOrganizations(APIView):
def get(self, request, format=None):
req = self.request
organizations = Organization.objects.all()
organization_serializer = OrganizationSerializer(organizations, many=True)
return Response({'organizations': organization_serializer.data})
class ListTargetsInOrganization(APIView):
def get(self, request, format=None):
req = self.request
organization_id = req.query_params.get('organization_id')
organization = Organization.objects.filter(id=organization_id)
targets = Domain.objects.filter(domains__in=organization)
organization_serializer = OrganizationSerializer(organization, many=True)
targets_serializer = OrganizationTargetsSerializer(targets, many=True)
return Response({'organization': organization_serializer.data, 'domains': targets_serializer.data})
class ListTargetsWithoutOrganization(APIView):
def get(self, request, format=None):
req = self.request
targets = Domain.objects.exclude(domains__in=Organization.objects.all())
targets_serializer = OrganizationTargetsSerializer(targets, many=True)
return Response({'domains': targets_serializer.data})
class VisualiseData(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
if scan_id:
mitch_data = ScanHistory.objects.filter(id=scan_id)
serializer = VisualiseDataSerializer(mitch_data, many=True)
return Response(serializer.data)
else:
return Response()
class ListTechnology(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
target_id = req.query_params.get('target_id')
if target_id:
tech = Technology.objects.filter(
technologies__in=Subdomain.objects.filter(
target_domain__id=target_id)).annotate(
count=Count('name')).order_by('-count')
serializer = TechnologyCountSerializer(tech, many=True)
return Response({"technologies": serializer.data})
elif scan_id:
tech = Technology.objects.filter(
technologies__in=Subdomain.objects.filter(
scan_history__id=scan_id)).annotate(
count=Count('name')).order_by('-count')
serializer = TechnologyCountSerializer(tech, many=True)
return Response({"technologies": serializer.data})
else:
tech = Technology.objects.filter(
technologies__in=Subdomain.objects.all()).annotate(
count=Count('name')).order_by('-count')
serializer = TechnologyCountSerializer(tech, many=True)
return Response({"technologies": serializer.data})
class ListDorkTypes(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
if scan_id:
dork = Dork.objects.filter(
dorks__in=ScanHistory.objects.filter(id=scan_id)
).values('type').annotate(count=Count('type')).order_by('-count')
serializer = DorkCountSerializer(dork, many=True)
return Response({"dorks": serializer.data})
else:
dork = Dork.objects.filter(
dorks__in=ScanHistory.objects.all()
).values('type').annotate(count=Count('type')).order_by('-count')
serializer = DorkCountSerializer(dork, many=True)
return Response({"dorks": serializer.data})
class ListEmails(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
if scan_id:
email = Email.objects.filter(
emails__in=ScanHistory.objects.filter(id=scan_id)).order_by('password')
serializer = EmailSerializer(email, many=True)
return Response({"emails": serializer.data})
class ListDorks(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
type = req.query_params.get('type')
if scan_id:
dork = Dork.objects.filter(
dorks__in=ScanHistory.objects.filter(id=scan_id))
else:
dork = Dork.objects.filter(
dorks__in=ScanHistory.objects.all())
if scan_id and type:
dork = dork.filter(type=type)
serializer = DorkSerializer(dork, many=True)
grouped_res = {}
for item in serializer.data:
item_type = item['type']
if item_type not in grouped_res:
grouped_res[item_type] = []
grouped_res[item_type].append(item)
return Response({"dorks": grouped_res})
class ListEmployees(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
if scan_id:
employee = Employee.objects.filter(
employees__in=ScanHistory.objects.filter(id=scan_id))
serializer = EmployeeSerializer(employee, many=True)
return Response({"employees": serializer.data})
class ListPorts(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
target_id = req.query_params.get('target_id')
ip_address = req.query_params.get('ip_address')
if target_id:
port = Port.objects.filter(
ports__in=IpAddress.objects.filter(
ip_addresses__in=Subdomain.objects.filter(
target_domain__id=target_id))).distinct()
elif scan_id:
port = Port.objects.filter(
ports__in=IpAddress.objects.filter(
ip_addresses__in=Subdomain.objects.filter(
scan_history__id=scan_id))).distinct()
else:
port = Port.objects.filter(
ports__in=IpAddress.objects.filter(
ip_addresses__in=Subdomain.objects.all())).distinct()
if ip_address:
port = port.filter(ports__address=ip_address).distinct()
serializer = PortSerializer(port, many=True)
return Response({"ports": serializer.data})
class ListSubdomains(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
project = req.query_params.get('project')
target_id = req.query_params.get('target_id')
ip_address = req.query_params.get('ip_address')
port = req.query_params.get('port')
tech = req.query_params.get('tech')
subdomains = Subdomain.objects.filter(target_domain__project__slug=project) if project else Subdomain.objects.all()
if scan_id:
subdomain_query = subdomains.filter(scan_history__id=scan_id).distinct('name')
elif target_id:
subdomain_query = subdomains.filter(target_domain__id=target_id).distinct('name')
else:
subdomain_query = subdomains.all().distinct('name')
if ip_address:
subdomain_query = subdomain_query.filter(ip_addresses__address=ip_address)
if tech:
subdomain_query = subdomain_query.filter(technologies__name=tech)
if port:
subdomain_query = subdomain_query.filter(
ip_addresses__in=IpAddress.objects.filter(
ports__in=Port.objects.filter(
number=port)))
if 'only_important' in req.query_params:
subdomain_query = subdomain_query.filter(is_important=True)
if 'no_lookup_interesting' in req.query_params:
serializer = OnlySubdomainNameSerializer(subdomain_query, many=True)
else:
serializer = SubdomainSerializer(subdomain_query, many=True)
return Response({"subdomains": serializer.data})
def post(self, req):
req = self.request
data = req.data
subdomain_ids = data.get('subdomain_ids')
subdomain_names = []
for id in subdomain_ids:
subdomain_names.append(Subdomain.objects.get(id=id).name)
if subdomain_names:
return Response({'status': True, "results": subdomain_names})
return Response({'status': False})
class ListOsintUsers(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
if scan_id:
documents = MetaFinderDocument.objects.filter(scan_history__id=scan_id).exclude(author__isnull=True).values('author').distinct()
serializer = MetafinderUserSerializer(documents, many=True)
return Response({"users": serializer.data})
class ListMetadata(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
if scan_id:
documents = MetaFinderDocument.objects.filter(scan_history__id=scan_id).distinct()
serializer = MetafinderDocumentSerializer(documents, many=True)
return Response({"metadata": serializer.data})
class ListIPs(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
target_id = req.query_params.get('target_id')
port = req.query_params.get('port')
if target_id:
ips = IpAddress.objects.filter(
ip_addresses__in=Subdomain.objects.filter(
target_domain__id=target_id)).distinct()
elif scan_id:
ips = IpAddress.objects.filter(
ip_addresses__in=Subdomain.objects.filter(
scan_history__id=scan_id)).distinct()
else:
ips = IpAddress.objects.filter(
ip_addresses__in=Subdomain.objects.all()).distinct()
if port:
ips = ips.filter(
ports__in=Port.objects.filter(
number=port)).distinct()
serializer = IpSerializer(ips, many=True)
return Response({"ips": serializer.data})
class IpAddressViewSet(viewsets.ModelViewSet):
queryset = Subdomain.objects.none()
serializer_class = IpSubdomainSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_id')
if scan_id:
self.queryset = Subdomain.objects.filter(
scan_history__id=scan_id).exclude(
ip_addresses__isnull=True).distinct()
else:
self.serializer_class = IpSerializer
self.queryset = IpAddress.objects.all()
return self.queryset
def paginate_queryset(self, queryset, view=None):
if 'no_page' in self.request.query_params:
return None
return self.paginator.paginate_queryset(
queryset, self.request, view=self)
class SubdomainsViewSet(viewsets.ModelViewSet):
queryset = Subdomain.objects.none()
serializer_class = SubdomainSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_id')
if scan_id:
if 'only_screenshot' in self.request.query_params:
return (
Subdomain.objects
.filter(scan_history__id=scan_id)
.exclude(screenshot_path__isnull=True))
return Subdomain.objects.filter(scan_history=scan_id)
def paginate_queryset(self, queryset, view=None):
if 'no_page' in self.request.query_params:
return None
return self.paginator.paginate_queryset(
queryset, self.request, view=self)
class SubdomainChangesViewSet(viewsets.ModelViewSet):
'''
This viewset will return the Subdomain changes
To get the new subdomains, we will look for ScanHistory with
subdomain_discovery = True and the status of the last scan has to be
successful and calculate difference
'''
queryset = Subdomain.objects.none()
serializer_class = SubdomainChangesSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_id')
changes = req.query_params.get('changes')
domain_id = ScanHistory.objects.filter(id=scan_id)[0].domain.id
scan_history_query = (
ScanHistory.objects
.filter(domain=domain_id)
.filter(tasks__overlap=['subdomain_discovery'])
.filter(id__lte=scan_id)
.exclude(Q(scan_status=-1) | Q(scan_status=1))
)
if scan_history_query.count() > 1:
last_scan = scan_history_query.order_by('-start_scan_date')[1]
scanned_host_q1 = (
Subdomain.objects
.filter(scan_history__id=scan_id)
.values('name')
)
scanned_host_q2 = (
Subdomain.objects
.filter(scan_history__id=last_scan.id)
.values('name')
)
added_subdomain = scanned_host_q1.difference(scanned_host_q2)
removed_subdomains = scanned_host_q2.difference(scanned_host_q1)
if changes == 'added':
return (
Subdomain.objects
.filter(scan_history=scan_id)
.filter(name__in=added_subdomain)
.annotate(
change=Value('added', output_field=CharField())
)
)
elif changes == 'removed':
return (
Subdomain.objects
.filter(scan_history=last_scan)
.filter(name__in=removed_subdomains)
.annotate(
change=Value('removed', output_field=CharField())
)
)
else:
added_subdomain = (
Subdomain.objects
.filter(scan_history=scan_id)
.filter(name__in=added_subdomain)
.annotate(
change=Value('added', output_field=CharField())
)
)
removed_subdomains = (
Subdomain.objects
.filter(scan_history=last_scan)
.filter(name__in=removed_subdomains)
.annotate(
change=Value('removed', output_field=CharField())
)
)
changes = added_subdomain.union(removed_subdomains)
return changes
return self.queryset
def paginate_queryset(self, queryset, view=None):
if 'no_page' in self.request.query_params:
return None
return self.paginator.paginate_queryset(
queryset, self.request, view=self)
class EndPointChangesViewSet(viewsets.ModelViewSet):
'''
This viewset will return the EndPoint changes
'''
queryset = EndPoint.objects.none()
serializer_class = EndPointChangesSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_id')
changes = req.query_params.get('changes')
domain_id = ScanHistory.objects.filter(id=scan_id).first().domain.id
scan_history = (
ScanHistory.objects
.filter(domain=domain_id)
.filter(tasks__overlap=['fetch_url'])
.filter(id__lte=scan_id)
.filter(scan_status=2)
)
if scan_history.count() > 1:
last_scan = scan_history.order_by('-start_scan_date')[1]
scanned_host_q1 = (
EndPoint.objects
.filter(scan_history__id=scan_id)
.values('http_url')
)
scanned_host_q2 = (
EndPoint.objects
.filter(scan_history__id=last_scan.id)
.values('http_url')
)
added_endpoints = scanned_host_q1.difference(scanned_host_q2)
removed_endpoints = scanned_host_q2.difference(scanned_host_q1)
if changes == 'added':
return (
EndPoint.objects
.filter(scan_history=scan_id)
.filter(http_url__in=added_endpoints)
.annotate(change=Value('added', output_field=CharField()))
)
elif changes == 'removed':
return (
EndPoint.objects
.filter(scan_history=last_scan)
.filter(http_url__in=removed_endpoints)
.annotate(change=Value('removed', output_field=CharField()))
)
else:
added_endpoints = (
EndPoint.objects
.filter(scan_history=scan_id)
.filter(http_url__in=added_endpoints)
.annotate(change=Value('added', output_field=CharField()))
)
removed_endpoints = (
EndPoint.objects
.filter(scan_history=last_scan)
.filter(http_url__in=removed_endpoints)
.annotate(change=Value('removed', output_field=CharField()))
)
changes = added_endpoints.union(removed_endpoints)
return changes
return self.queryset
def paginate_queryset(self, queryset, view=None):
if 'no_page' in self.request.query_params:
return None
return self.paginator.paginate_queryset(
queryset, self.request, view=self)
class InterestingSubdomainViewSet(viewsets.ModelViewSet):
queryset = Subdomain.objects.none()
serializer_class = SubdomainSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_id')
domain_id = req.query_params.get('target_id')
if 'only_subdomains' in self.request.query_params:
self.serializer_class = InterestingSubdomainSerializer
if scan_id:
self.queryset = get_interesting_subdomains(scan_history=scan_id)
elif domain_id:
self.queryset = get_interesting_subdomains(domain_id=domain_id)
else:
self.queryset = get_interesting_subdomains()
return self.queryset
def filter_queryset(self, qs):
qs = self.queryset.filter()
search_value = self.request.GET.get(u'search[value]', None)
_order_col = self.request.GET.get(u'order[0][column]', None)
_order_direction = self.request.GET.get(u'order[0][dir]', None)
order_col = 'content_length'
if _order_col == '0':
order_col = 'name'
elif _order_col == '1':
order_col = 'page_title'
elif _order_col == '2':
order_col = 'http_status'
elif _order_col == '3':
order_col = 'content_length'
if _order_direction == 'desc':
order_col = f'-{order_col}'
if search_value:
qs = self.queryset.filter(
Q(name__icontains=search_value) |
Q(page_title__icontains=search_value) |
Q(http_status__icontains=search_value)
)
return qs.order_by(order_col)
def paginate_queryset(self, queryset, view=None):
if 'no_page' in self.request.query_params:
return None
return self.paginator.paginate_queryset(
queryset, self.request, view=self)
class InterestingEndpointViewSet(viewsets.ModelViewSet):
queryset = EndPoint.objects.none()
serializer_class = EndpointSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_id')
target_id = req.query_params.get('target_id')
if 'only_endpoints' in self.request.query_params:
self.serializer_class = InterestingEndPointSerializer
if scan_id:
return get_interesting_endpoints(scan_history=scan_id)
elif target_id:
return get_interesting_endpoints(target=target_id)
else:
return get_interesting_endpoints()
def paginate_queryset(self, queryset, view=None):
if 'no_page' in self.request.query_params:
return None
return self.paginator.paginate_queryset(
queryset, self.request, view=self)
class SubdomainDatatableViewSet(viewsets.ModelViewSet):
queryset = Subdomain.objects.none()
serializer_class = SubdomainSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_id')
target_id = req.query_params.get('target_id')
url_query = req.query_params.get('query_param')
ip_address = req.query_params.get('ip_address')
name = req.query_params.get('name')
project = req.query_params.get('project')
subdomains = Subdomain.objects.filter(target_domain__project__slug=project)
if 'is_important' in req.query_params:
subdomains = subdomains.filter(is_important=True)
if target_id:
self.queryset = (
subdomains
.filter(target_domain__id=target_id)
.distinct()
)
elif url_query:
self.queryset = (
subdomains
.filter(Q(target_domain__name=url_query))
.distinct()
)
elif scan_id:
self.queryset = (
subdomains
.filter(scan_history__id=scan_id)
.distinct()
)
else:
self.queryset = subdomains.distinct()
if 'only_directory' in req.query_params:
self.queryset = self.queryset.exclude(directories__isnull=True)
if ip_address:
self.queryset = self.queryset.filter(ip_addresses__address__icontains=ip_address)
if name:
self.queryset = self.queryset.filter(name=name)
return self.queryset
def filter_queryset(self, qs):
qs = self.queryset.filter()
search_value = self.request.GET.get(u'search[value]', None)
_order_col = self.request.GET.get(u'order[0][column]', None)
_order_direction = self.request.GET.get(u'order[0][dir]', None)
order_col = 'content_length'
if _order_col == '0':
order_col = 'checked'
elif _order_col == '1':
order_col = 'name'
elif _order_col == '4':
order_col = 'http_status'
elif _order_col == '5':
order_col = 'page_title'
elif _order_col == '8':
order_col = 'content_length'
elif _order_col == '10':
order_col = 'response_time'
if _order_direction == 'desc':
order_col = f'-{order_col}'
# if the search query is separated by = means, it is a specific lookup
# divide the search query into two half and lookup
if search_value:
operators = ['=', '&', '|', '>', '<', '!']
if any(x in search_value for x in operators):
if '&' in search_value:
complex_query = search_value.split('&')
for query in complex_query:
if query.strip():
qs = qs & self.special_lookup(query.strip())
elif '|' in search_value:
qs = Subdomain.objects.none()
complex_query = search_value.split('|')
for query in complex_query:
if query.strip():
qs = self.special_lookup(query.strip()) | qs
else:
qs = self.special_lookup(search_value)
else:
qs = self.general_lookup(search_value)
return qs.order_by(order_col)
def general_lookup(self, search_value):
qs = self.queryset.filter(
Q(name__icontains=search_value) |
Q(cname__icontains=search_value) |
Q(http_status__icontains=search_value) |
Q(page_title__icontains=search_value) |
Q(http_url__icontains=search_value) |
Q(technologies__name__icontains=search_value) |
Q(webserver__icontains=search_value) |
Q(ip_addresses__address__icontains=search_value) |
Q(ip_addresses__ports__number__icontains=search_value) |
Q(ip_addresses__ports__service_name__icontains=search_value) |
Q(ip_addresses__ports__description__icontains=search_value)
)
if 'only_directory' in self.request.query_params:
qs = qs | self.queryset.filter(
Q(directories__directory_files__name__icontains=search_value)
)
return qs
def special_lookup(self, search_value):
qs = self.queryset.filter()
if '=' in search_value:
search_param = search_value.split("=")
title = search_param[0].lower().strip()
content = search_param[1].lower().strip()
if 'name' in title:
qs = self.queryset.filter(name__icontains=content)
elif 'page_title' in title:
qs = self.queryset.filter(page_title__icontains=content)
elif 'http_url' in title:
qs = self.queryset.filter(http_url__icontains=content)
elif 'content_type' in title:
qs = self.queryset.filter(content_type__icontains=content)
elif 'cname' in title:
qs = self.queryset.filter(cname__icontains=content)
elif 'webserver' in title:
qs = self.queryset.filter(webserver__icontains=content)
elif 'ip_addresses' in title:
qs = self.queryset.filter(
ip_addresses__address__icontains=content)
elif 'is_important' in title:
if 'true' in content.lower():
qs = self.queryset.filter(is_important=True)
else:
qs = self.queryset.filter(is_important=False)
elif 'port' in title:
qs = (
self.queryset
.filter(ip_addresses__ports__number__icontains=content)
|
self.queryset
.filter(ip_addresses__ports__service_name__icontains=content)
|
self.queryset
.filter(ip_addresses__ports__description__icontains=content)
)
elif 'technology' in title:
qs = (
self.queryset
.filter(technologies__name__icontains=content)
)
elif 'http_status' in title:
try:
int_http_status = int(content)
qs = self.queryset.filter(http_status=int_http_status)
except Exception as e:
print(e)
elif 'content_length' in title:
try:
int_http_status = int(content)
qs = self.queryset.filter(content_length=int_http_status)
except Exception as e:
print(e)
elif '>' in search_value:
search_param = search_value.split(">")
title = search_param[0].lower().strip()
content = search_param[1].lower().strip()
if 'http_status' in title:
try:
int_val = int(content)
qs = self.queryset.filter(http_status__gt=int_val)
except Exception as e:
print(e)
elif 'content_length' in title:
try:
int_val = int(content)
qs = self.queryset.filter(content_length__gt=int_val)
except Exception as e:
print(e)
elif '<' in search_value:
search_param = search_value.split("<")
title = search_param[0].lower().strip()
content = search_param[1].lower().strip()
if 'http_status' in title:
try:
int_val = int(content)
qs = self.queryset.filter(http_status__lt=int_val)
except Exception as e:
print(e)
elif 'content_length' in title:
try:
int_val = int(content)
qs = self.queryset.filter(content_length__lt=int_val)
except Exception as e:
print(e)
elif '!' in search_value:
search_param = search_value.split("!")
title = search_param[0].lower().strip()
content = search_param[1].lower().strip()
if 'name' in title:
qs = self.queryset.exclude(name__icontains=content)
elif 'page_title' in title:
qs = self.queryset.exclude(page_title__icontains=content)
elif 'http_url' in title:
qs = self.queryset.exclude(http_url__icontains=content)
elif 'content_type' in title:
qs = (
self.queryset
.exclude(content_type__icontains=content)
)
elif 'cname' in title:
qs = self.queryset.exclude(cname__icontains=content)
elif 'webserver' in title:
qs = self.queryset.exclude(webserver__icontains=content)
elif 'ip_addresses' in title:
qs = self.queryset.exclude(
ip_addresses__address__icontains=content)
elif 'port' in title:
qs = (
self.queryset
.exclude(ip_addresses__ports__number__icontains=content)
|
self.queryset
.exclude(ip_addresses__ports__service_name__icontains=content)
|
self.queryset
.exclude(ip_addresses__ports__description__icontains=content)
)
elif 'technology' in title:
qs = (
self.queryset
.exclude(technologies__name__icontains=content)
)
elif 'http_status' in title:
try:
int_http_status = int(content)
qs = self.queryset.exclude(http_status=int_http_status)
except Exception as e:
print(e)
elif 'content_length' in title:
try:
int_http_status = int(content)
qs = self.queryset.exclude(content_length=int_http_status)
except Exception as e:
print(e)
return qs
class ListActivityLogsViewSet(viewsets.ModelViewSet):
serializer_class = CommandSerializer
queryset = Command.objects.none()
def get_queryset(self):
req = self.request
activity_id = req.query_params.get('activity_id')
self.queryset = Command.objects.filter(activity__id=activity_id)
return self.queryset
class ListScanLogsViewSet(viewsets.ModelViewSet):
serializer_class = CommandSerializer
queryset = Command.objects.none()
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_id')
self.queryset = Command.objects.filter(scan_history__id=scan_id)
return self.queryset
class ListEndpoints(APIView):
def get(self, request, format=None):
req = self.request
scan_id = req.query_params.get('scan_id')
target_id = req.query_params.get('target_id')
subdomain_name = req.query_params.get('subdomain_name')
pattern = req.query_params.get('pattern')
if scan_id:
endpoints = (
EndPoint.objects
.filter(scan_history__id=scan_id)
)
elif target_id:
endpoints = (
EndPoint.objects
.filter(target_domain__id=target_id)
.distinct()
)
else:
endpoints = EndPoint.objects.all()
if subdomain_name:
endpoints = endpoints.filter(subdomain__name=subdomain_name)
if pattern:
endpoints = endpoints.filter(matched_gf_patterns__icontains=pattern)
if 'only_urls' in req.query_params:
endpoints_serializer = EndpointOnlyURLsSerializer(endpoints, many=True)
else:
endpoints_serializer = EndpointSerializer(endpoints, many=True)
return Response({'endpoints': endpoints_serializer.data})
class EndPointViewSet(viewsets.ModelViewSet):
queryset = EndPoint.objects.none()
serializer_class = EndpointSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_history')
target_id = req.query_params.get('target_id')
url_query = req.query_params.get('query_param')
subdomain_id = req.query_params.get('subdomain_id')
project = req.query_params.get('project')
endpoints_obj = EndPoint.objects.filter(scan_history__domain__project__slug=project)
gf_tag = req.query_params.get(
'gf_tag') if 'gf_tag' in req.query_params else None
if scan_id:
endpoints = (
endpoints_obj
.filter(scan_history__id=scan_id)
.distinct()
)
else:
endpoints = endpoints_obj.distinct()
if url_query:
endpoints = (
endpoints
.filter(Q(target_domain__name=url_query))
.distinct()
)
if gf_tag:
endpoints = endpoints.filter(matched_gf_patterns__icontains=gf_tag)
if target_id:
endpoints = endpoints.filter(target_domain__id=target_id)
if subdomain_id:
endpoints = endpoints.filter(subdomain__id=subdomain_id)
if 'only_urls' in req.query_params:
self.serializer_class = EndpointOnlyURLsSerializer
# Filter status code 404 and 0
# endpoints = (
# endpoints
# .exclude(http_status=0)
# .exclude(http_status=None)
# .exclude(http_status=404)
# )
self.queryset = endpoints
return self.queryset
def filter_queryset(self, qs):
qs = self.queryset.filter()
search_value = self.request.GET.get(u'search[value]', None)
_order_col = self.request.GET.get(u'order[0][column]', None)
_order_direction = self.request.GET.get(u'order[0][dir]', None)
if search_value or _order_col or _order_direction:
order_col = 'content_length'
if _order_col == '1':
order_col = 'http_url'
elif _order_col == '2':
order_col = 'http_status'
elif _order_col == '3':
order_col = 'page_title'
elif _order_col == '4':
order_col = 'matched_gf_patterns'
elif _order_col == '5':
order_col = 'content_type'
elif _order_col == '6':
order_col = 'content_length'
elif _order_col == '7':
order_col = 'techs'
elif _order_col == '8':
order_col = 'webserver'
elif _order_col == '9':
order_col = 'response_time'
if _order_direction == 'desc':
order_col = f'-{order_col}'
# if the search query is separated by = means, it is a specific lookup
# divide the search query into two half and lookup
if '=' in search_value or '&' in search_value or '|' in search_value or '>' in search_value or '<' in search_value or '!' in search_value:
if '&' in search_value:
complex_query = search_value.split('&')
for query in complex_query:
if query.strip():
qs = qs & self.special_lookup(query.strip())
elif '|' in search_value:
qs = Subdomain.objects.none()
complex_query = search_value.split('|')
for query in complex_query:
if query.strip():
qs = self.special_lookup(query.strip()) | qs
else:
qs = self.special_lookup(search_value)
else:
qs = self.general_lookup(search_value)
return qs.order_by(order_col)
return qs
def general_lookup(self, search_value):
return \
self.queryset.filter(Q(http_url__icontains=search_value) |
Q(page_title__icontains=search_value) |
Q(http_status__icontains=search_value) |
Q(content_type__icontains=search_value) |
Q(webserver__icontains=search_value) |
Q(techs__name__icontains=search_value) |
Q(content_type__icontains=search_value) |
Q(matched_gf_patterns__icontains=search_value))
def special_lookup(self, search_value):
qs = self.queryset.filter()
if '=' in search_value:
search_param = search_value.split("=")
lookup_title = search_param[0].lower().strip()
lookup_content = search_param[1].lower().strip()
if 'http_url' in lookup_title:
qs = self.queryset.filter(http_url__icontains=lookup_content)
elif 'page_title' in lookup_title:
qs = (
self.queryset
.filter(page_title__icontains=lookup_content)
)
elif 'content_type' in lookup_title:
qs = (
self.queryset
.filter(content_type__icontains=lookup_content)
)
elif 'webserver' in lookup_title:
qs = self.queryset.filter(webserver__icontains=lookup_content)
elif 'technology' in lookup_title:
qs = (
self.queryset
.filter(techs__name__icontains=lookup_content)
)
elif 'gf_pattern' in lookup_title:
qs = (
self.queryset
.filter(matched_gf_patterns__icontains=lookup_content)
)
elif 'http_status' in lookup_title:
try:
int_http_status = int(lookup_content)
qs = self.queryset.filter(http_status=int_http_status)
except Exception as e:
print(e)
elif 'content_length' in lookup_title:
try:
int_http_status = int(lookup_content)
qs = self.queryset.filter(content_length=int_http_status)
except Exception as e:
print(e)
elif '>' in search_value:
search_param = search_value.split(">")
lookup_title = search_param[0].lower().strip()
lookup_content = search_param[1].lower().strip()
if 'http_status' in lookup_title:
try:
int_val = int(lookup_content)
qs = (
self.queryset
.filter(http_status__gt=int_val)
)
except Exception as e:
print(e)
elif 'content_length' in lookup_title:
try:
int_val = int(lookup_content)
qs = self.queryset.filter(content_length__gt=int_val)
except Exception as e:
print(e)
elif '<' in search_value:
search_param = search_value.split("<")
lookup_title = search_param[0].lower().strip()
lookup_content = search_param[1].lower().strip()
if 'http_status' in lookup_title:
try:
int_val = int(lookup_content)
qs = self.queryset.filter(http_status__lt=int_val)
except Exception as e:
print(e)
elif 'content_length' in lookup_title:
try:
int_val = int(lookup_content)
qs = self.queryset.filter(content_length__lt=int_val)
except Exception as e:
print(e)
elif '!' in search_value:
search_param = search_value.split("!")
lookup_title = search_param[0].lower().strip()
lookup_content = search_param[1].lower().strip()
if 'http_url' in lookup_title:
qs = (
self.queryset
.exclude(http_url__icontains=lookup_content)
)
elif 'page_title' in lookup_title:
qs = (
self.queryset
.exclude(page_title__icontains=lookup_content)
)
elif 'content_type' in lookup_title:
qs = (
self.queryset
.exclude(content_type__icontains=lookup_content)
)
elif 'webserver' in lookup_title:
qs = (
self.queryset
.exclude(webserver__icontains=lookup_content)
)
elif 'technology' in lookup_title:
qs = (
self.queryset
.exclude(techs__name__icontains=lookup_content)
)
elif 'gf_pattern' in lookup_title:
qs = (
self.queryset
.exclude(matched_gf_patterns__icontains=lookup_content)
)
elif 'http_status' in lookup_title:
try:
int_http_status = int(lookup_content)
qs = self.queryset.exclude(http_status=int_http_status)
except Exception as e:
print(e)
elif 'content_length' in lookup_title:
try:
int_http_status = int(lookup_content)
qs = self.queryset.exclude(content_length=int_http_status)
except Exception as e:
print(e)
return qs
class DirectoryViewSet(viewsets.ModelViewSet):
queryset = DirectoryFile.objects.none()
serializer_class = DirectoryFileSerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_history')
subdomain_id = req.query_params.get('subdomain_id')
subdomains = None
if not (scan_id or subdomain_id):
return Response({
'status': False,
'message': 'Scan id or subdomain id must be provided.'
})
elif scan_id:
subdomains = Subdomain.objects.filter(scan_history__id=scan_id)
elif subdomain_id:
subdomains = Subdomain.objects.filter(id=subdomain_id)
dirs_scans = DirectoryScan.objects.filter(directories__in=subdomains)
qs = (
DirectoryFile.objects
.filter(directory_files__in=dirs_scans)
.distinct()
)
self.queryset = qs
return self.queryset
class VulnerabilityViewSet(viewsets.ModelViewSet):
queryset = Vulnerability.objects.none()
serializer_class = VulnerabilitySerializer
def get_queryset(self):
req = self.request
scan_id = req.query_params.get('scan_history')
target_id = req.query_params.get('target_id')
domain = req.query_params.get('domain')
severity = req.query_params.get('severity')
subdomain_id = req.query_params.get('subdomain_id')
subdomain_name = req.query_params.get('subdomain')
vulnerability_name = req.query_params.get('vulnerability_name')
slug = self.request.GET.get('project', None)
if slug:
vulnerabilities = Vulnerability.objects.filter(scan_history__domain__project__slug=slug)
else:
vulnerabilities = Vulnerability.objects.all()
if scan_id:
qs = (
vulnerabilities
.filter(scan_history__id=scan_id)
.distinct()
)
elif target_id:
qs = (
vulnerabilities
.filter(target_domain__id=target_id)
.distinct()
)
elif subdomain_name:
subdomains = Subdomain.objects.filter(name=subdomain_name)
qs = (
vulnerabilities
.filter(subdomain__in=subdomains)
.distinct()
)
else:
qs = vulnerabilities.distinct()
if domain:
qs = qs.filter(Q(target_domain__name=domain)).distinct()
if vulnerability_name:
qs = qs.filter(Q(name=vulnerability_name)).distinct()
if severity:
qs = qs.filter(severity=severity)
if subdomain_id:
qs = qs.filter(subdomain__id=subdomain_id)
self.queryset = qs
return self.queryset
def filter_queryset(self, qs):
qs = self.queryset.filter()
search_value = self.request.GET.get(u'search[value]', None)
_order_col = self.request.GET.get(u'order[0][column]', None)
_order_direction = self.request.GET.get(u'order[0][dir]', None)
if search_value or _order_col or _order_direction:
order_col = 'severity'
if _order_col == '1':
order_col = 'source'
elif _order_col == '3':
order_col = 'name'
elif _order_col == '7':
order_col = 'severity'
elif _order_col == '11':
order_col = 'http_url'
elif _order_col == '15':
order_col = 'open_status'
if _order_direction == 'desc':
order_col = f'-{order_col}'
# if the search query is separated by = means, it is a specific lookup
# divide the search query into two half and lookup
operators = ['=', '&', '|', '>', '<', '!']
if any(x in search_value for x in operators):
if '&' in search_value:
complex_query = search_value.split('&')
for query in complex_query:
if query.strip():
qs = qs & self.special_lookup(query.strip())
elif '|' in search_value:
qs = Subdomain.objects.none()
complex_query = search_value.split('|')
for query in complex_query:
if query.strip():
qs = self.special_lookup(query.strip()) | qs
else:
qs = self.special_lookup(search_value)
else:
qs = self.general_lookup(search_value)
return qs.order_by(order_col)
return qs.order_by('-severity')
def general_lookup(self, search_value):
qs = (
self.queryset
.filter(Q(http_url__icontains=search_value) |
Q(target_domain__name__icontains=search_value) |
Q(template__icontains=search_value) |
Q(template_id__icontains=search_value) |
Q(name__icontains=search_value) |
Q(severity__icontains=search_value) |
Q(description__icontains=search_value) |
Q(extracted_results__icontains=search_value) |
Q(references__url__icontains=search_value) |
Q(cve_ids__name__icontains=search_value) |
Q(cwe_ids__name__icontains=search_value) |
Q(cvss_metrics__icontains=search_value) |
Q(cvss_score__icontains=search_value) |
Q(type__icontains=search_value) |
Q(open_status__icontains=search_value) |
Q(hackerone_report_id__icontains=search_value) |
Q(tags__name__icontains=search_value))
)
return qs
def special_lookup(self, search_value):
qs = self.queryset.filter()
if '=' in search_value:
search_param = search_value.split("=")
lookup_title = search_param[0].lower().strip()
lookup_content = search_param[1].lower().strip()
if 'severity' in lookup_title:
severity_value = NUCLEI_SEVERITY_MAP.get(lookup_content, -1)
qs = (
self.queryset
.filter(severity=severity_value)
)
elif 'name' in lookup_title:
qs = (
self.queryset
.filter(name__icontains=lookup_content)
)
elif 'http_url' in lookup_title:
qs = (
self.queryset
.filter(http_url__icontains=lookup_content)
)
elif 'template' in lookup_title:
qs = (
self.queryset
.filter(template__icontains=lookup_content)
)
elif 'template_id' in lookup_title:
qs = (
self.queryset
.filter(template_id__icontains=lookup_content)
)
elif 'cve_id' in lookup_title or 'cve' in lookup_title:
qs = (
self.queryset
.filter(cve_ids__name__icontains=lookup_content)
)
elif 'cwe_id' in lookup_title or 'cwe' in lookup_title:
qs = (
self.queryset
.filter(cwe_ids__name__icontains=lookup_content)
)
elif 'cvss_metrics' in lookup_title:
qs = (
self.queryset
.filter(cvss_metrics__icontains=lookup_content)
)
elif 'cvss_score' in lookup_title:
qs = (
self.queryset
.filter(cvss_score__exact=lookup_content)
)
elif 'type' in lookup_title:
qs = (
self.queryset
.filter(type__icontains=lookup_content)
)
elif 'tag' in lookup_title:
qs = (
self.queryset
.filter(tags__name__icontains=lookup_content)
)
elif 'status' in lookup_title:
open_status = lookup_content == 'open'
qs = (
self.queryset
.filter(open_status=open_status)
)
elif 'description' in lookup_title:
qs = (
self.queryset
.filter(Q(description__icontains=lookup_content) |
Q(template__icontains=lookup_content) |
Q(extracted_results__icontains=lookup_content))
)
elif '!' in search_value:
search_param = search_value.split("!")
lookup_title = search_param[0].lower().strip()
lookup_content = search_param[1].lower().strip()
if 'severity' in lookup_title:
severity_value = NUCLEI_SEVERITY_MAP.get(lookup_title, -1)
qs = (
self.queryset
.exclude(severity=severity_value)
)
elif 'name' in lookup_title:
qs = (
self.queryset
.exclude(name__icontains=lookup_content)
)
elif 'http_url' in lookup_title:
qs = (
self.queryset
.exclude(http_url__icontains=lookup_content)
)
elif 'template' in lookup_title:
qs = (
self.queryset
.exclude(template__icontains=lookup_content)
)
elif 'template_id' in lookup_title:
qs = (
self.queryset
.exclude(template_id__icontains=lookup_content)
)
elif 'cve_id' in lookup_title or 'cve' in lookup_title:
qs = (
self.queryset
.exclude(cve_ids__icontains=lookup_content)
)
elif 'cwe_id' in lookup_title or 'cwe' in lookup_title:
qs = (
self.queryset
.exclude(cwe_ids__icontains=lookup_content)
)
elif 'cvss_metrics' in lookup_title:
qs = (
self.queryset
.exclude(cvss_metrics__icontains=lookup_content)
)
elif 'cvss_score' in lookup_title:
qs = (
self.queryset
.exclude(cvss_score__exact=lookup_content)
)
elif 'type' in lookup_title:
qs = (
self.queryset
.exclude(type__icontains=lookup_content)
)
elif 'tag' in lookup_title:
qs = (
self.queryset
.exclude(tags__icontains=lookup_content)
)
elif 'status' in lookup_title:
open_status = lookup_content == 'open'
qs = (
self.queryset
.exclude(open_status=open_status)
)
elif 'description' in lookup_title:
qs = (
self.queryset
.exclude(Q(description__icontains=lookup_content) |
Q(template__icontains=lookup_content) |
Q(extracted_results__icontains=lookup_content))
)
elif '>' in search_value:
search_param = search_value.split(">")
lookup_title = search_param[0].lower().strip()
lookup_content = search_param[1].lower().strip()
if 'cvss_score' in lookup_title:
try:
val = float(lookup_content)
qs = self.queryset.filter(cvss_score__gt=val)
except Exception as e:
print(e)
elif '<' in search_value:
search_param = search_value.split("<")
lookup_title = search_param[0].lower().strip()
lookup_content = search_param[1].lower().strip()
if 'cvss_score' in lookup_title:
try:
val = int(lookup_content)
qs = self.queryset.filter(cvss_score__lt=val)
except Exception as e:
print(e)
return qs