ai scans schedule

This commit is contained in:
usmannasir
2025-07-06 23:22:40 +05:00
parent db768f9a0a
commit 45760af539
8 changed files with 1338 additions and 1 deletions

View File

View File

@@ -0,0 +1,330 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from datetime import datetime, timedelta
import json
import time
class Command(BaseCommand):
help = 'Run scheduled AI security scans'
def add_arguments(self, parser):
parser.add_argument(
'--daemon',
action='store_true',
help='Run as daemon, checking for scheduled scans every minute',
)
parser.add_argument(
'--scan-id',
type=int,
help='Run a specific scheduled scan by ID',
)
def handle(self, *args, **options):
if options['daemon']:
self.stdout.write('Starting scheduled scan daemon...')
self.run_daemon()
elif options['scan_id']:
self.stdout.write(f'Running scheduled scan ID {options["scan_id"]}...')
self.run_scheduled_scan_by_id(options['scan_id'])
else:
self.stdout.write('Checking for scheduled scans to run...')
self.check_and_run_scans()
def run_daemon(self):
"""Run as daemon, checking for scans every minute"""
while True:
try:
self.check_and_run_scans()
time.sleep(60) # Check every minute
except KeyboardInterrupt:
self.stdout.write('Daemon stopped by user')
break
except Exception as e:
self.stderr.write(f'Error in daemon: {str(e)}')
time.sleep(60) # Continue after error
def check_and_run_scans(self):
"""Check for scheduled scans that need to run"""
from aiScanner.models import ScheduledScan
now = timezone.now()
# Find scans that are due to run
due_scans = ScheduledScan.objects.filter(
status='active',
next_run__lte=now
)
for scan in due_scans:
self.stdout.write(f'Running scheduled scan: {scan.name} (ID: {scan.id})')
self.execute_scheduled_scan(scan)
def run_scheduled_scan_by_id(self, scan_id):
"""Run a specific scheduled scan by ID"""
from aiScanner.models import ScheduledScan
try:
scan = ScheduledScan.objects.get(id=scan_id)
self.stdout.write(f'Running scheduled scan: {scan.name}')
self.execute_scheduled_scan(scan)
except ScheduledScan.DoesNotExist:
self.stderr.write(f'Scheduled scan with ID {scan_id} not found')
def execute_scheduled_scan(self, scheduled_scan):
"""Execute a scheduled scan"""
from aiScanner.models import ScheduledScanExecution, ScanHistory
from aiScanner.aiScannerManager import AIScannerManager
from loginSystem.models import Administrator
from websiteFunctions.models import Websites
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
# Create execution record
execution = ScheduledScanExecution.objects.create(
scheduled_scan=scheduled_scan,
status='running',
started_at=timezone.now()
)
try:
# Update last run time
scheduled_scan.last_run = timezone.now()
scheduled_scan.next_run = scheduled_scan.calculate_next_run()
scheduled_scan.save()
# Get domains to scan
domains_to_scan = []
admin = scheduled_scan.admin
# Validate domains still exist and user has access
for domain in scheduled_scan.domain_list:
try:
website = Websites.objects.get(domain=domain, admin=admin)
domains_to_scan.append(domain)
except Websites.DoesNotExist:
logging.writeToFile(f'[Scheduled Scan] Domain {domain} no longer accessible for user {admin.userName}')
continue
if not domains_to_scan:
execution.status = 'failed'
execution.error_message = 'No accessible domains found for scanning'
execution.completed_at = timezone.now()
execution.save()
self.stderr.write(f'No accessible domains for scheduled scan {scheduled_scan.name}')
return
execution.set_scanned_domains(domains_to_scan)
execution.total_scans = len(domains_to_scan)
execution.save()
# Initialize scanner manager
sm = AIScannerManager()
scan_ids = []
successful_scans = 0
failed_scans = 0
total_cost = 0.0
# Execute scans for each domain
for domain in domains_to_scan:
try:
self.stdout.write(f'Starting scan for domain: {domain}')
# Create a fake request object for the scanner manager
class FakeRequest:
def __init__(self, admin_id):
self.session = {'userID': admin_id}
self.method = 'POST'
self.POST = {
'domain': domain,
'scan_type': scheduled_scan.scan_type
}
fake_request = FakeRequest(admin.pk)
# Start the scan
result = sm.startScan(fake_request, admin.pk)
if hasattr(result, 'content'):
# It's an HTTP response, parse the JSON
import json
response_data = json.loads(result.content.decode('utf-8'))
else:
# It's already a dict
response_data = result
if response_data.get('success'):
scan_id = response_data.get('scan_id')
if scan_id:
scan_ids.append(scan_id)
successful_scans += 1
# Get cost estimate if available
if 'cost_estimate' in response_data:
total_cost += float(response_data['cost_estimate'])
logging.writeToFile(f'[Scheduled Scan] Successfully started scan {scan_id} for {domain}')
else:
failed_scans += 1
logging.writeToFile(f'[Scheduled Scan] Failed to get scan ID for {domain}')
else:
failed_scans += 1
error_msg = response_data.get('error', 'Unknown error')
logging.writeToFile(f'[Scheduled Scan] Failed to start scan for {domain}: {error_msg}')
except Exception as e:
failed_scans += 1
error_msg = str(e)
logging.writeToFile(f'[Scheduled Scan] Exception starting scan for {domain}: {error_msg}')
# Small delay between scans to avoid overwhelming the system
time.sleep(2)
# Update execution record
execution.successful_scans = successful_scans
execution.failed_scans = failed_scans
execution.total_cost = total_cost
execution.set_scan_ids(scan_ids)
execution.status = 'completed' if failed_scans == 0 else 'completed' # Always completed if we tried all
execution.completed_at = timezone.now()
execution.save()
# Send notifications if configured
if scheduled_scan.email_notifications:
self.send_notifications(scheduled_scan, execution)
self.stdout.write(
f'Scheduled scan completed: {successful_scans} successful, {failed_scans} failed'
)
except Exception as e:
# Update execution record with error
execution.status = 'failed'
execution.error_message = str(e)
execution.completed_at = timezone.now()
execution.save()
logging.writeToFile(f'[Scheduled Scan] Failed to execute scheduled scan {scheduled_scan.name}: {str(e)}')
self.stderr.write(f'Failed to execute scheduled scan {scheduled_scan.name}: {str(e)}')
# Send failure notification
if scheduled_scan.email_notifications and scheduled_scan.notify_on_failure:
self.send_failure_notification(scheduled_scan, str(e))
def send_notifications(self, scheduled_scan, execution):
"""Send email notifications for completed scan"""
try:
# Determine if we should send notification
should_notify = False
if execution.status == 'failed' and scheduled_scan.notify_on_failure:
should_notify = True
elif execution.status == 'completed':
if scheduled_scan.notify_on_completion:
should_notify = True
elif scheduled_scan.notify_on_threats and execution.successful_scans > 0:
# Check if any scans found threats
# This would require checking the scan results, which might not be available immediately
# For now, we'll just send completion notifications
should_notify = scheduled_scan.notify_on_completion
if should_notify:
self.send_execution_notification(scheduled_scan, execution)
except Exception as e:
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
logging.writeToFile(f'[Scheduled Scan] Failed to send notification: {str(e)}')
def send_execution_notification(self, scheduled_scan, execution):
"""Send notification email for scan execution"""
try:
# Get notification emails
notification_emails = scheduled_scan.notification_email_list
if not notification_emails:
# Use admin email as fallback
notification_emails = [scheduled_scan.admin.email] if scheduled_scan.admin.email else []
if not notification_emails:
return
# Prepare email content
subject = f'AI Scanner: Scheduled Scan "{scheduled_scan.name}" Completed'
status_text = execution.status.title()
if execution.status == 'completed':
if execution.failed_scans == 0:
status_text = 'Completed Successfully'
else:
status_text = f'Completed with {execution.failed_scans} failures'
message = f"""
Scheduled AI Security Scan Report
Scan Name: {scheduled_scan.name}
Status: {status_text}
Execution Time: {execution.execution_time.strftime('%Y-%m-%d %H:%M:%S UTC')}
Results:
- Total Domains: {execution.total_scans}
- Successful Scans: {execution.successful_scans}
- Failed Scans: {execution.failed_scans}
- Total Cost: ${execution.total_cost:.4f}
Domains Scanned: {', '.join(execution.scanned_domains)}
{f'Error Message: {execution.error_message}' if execution.error_message else ''}
Scan IDs: {', '.join(execution.scan_id_list)}
View detailed results in your CyberPanel AI Scanner dashboard.
"""
# Send email using CyberPanel's email system
from plogical.mailUtilities import mailUtilities
mailUtilities.sendEmail(notification_emails[0], subject, message)
# Log notification sent
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
logging.writeToFile(f'[Scheduled Scan] Notification sent for {scheduled_scan.name} to {len(notification_emails)} recipients')
except Exception as e:
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
logging.writeToFile(f'[Scheduled Scan] Failed to send notification email: {str(e)}')
def send_failure_notification(self, scheduled_scan, error_message):
"""Send notification email for scan failure"""
try:
# Get notification emails
notification_emails = scheduled_scan.notification_email_list
if not notification_emails:
# Use admin email as fallback
notification_emails = [scheduled_scan.admin.email] if scheduled_scan.admin.email else []
if not notification_emails:
return
# Prepare email content
subject = f'AI Scanner: Scheduled Scan "{scheduled_scan.name}" Failed'
message = f"""
Scheduled AI Security Scan Failure
Scan Name: {scheduled_scan.name}
Status: Failed
Time: {timezone.now().strftime('%Y-%m-%d %H:%M:%S UTC')}
Error: {error_message}
Please check your CyberPanel AI Scanner configuration and try again.
"""
# Send email using CyberPanel's email system
from plogical.mailUtilities import mailUtilities
mailUtilities.sendEmail(notification_emails[0], subject, message)
# Log notification sent
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
logging.writeToFile(f'[Scheduled Scan] Failure notification sent for {scheduled_scan.name}')
except Exception as e:
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
logging.writeToFile(f'[Scheduled Scan] Failed to send failure notification email: {str(e)}')

View File

@@ -107,3 +107,216 @@ class FileAccessToken(models.Model):
def is_expired(self):
from django.utils import timezone
return timezone.now() > self.expires_at
class ScheduledScan(models.Model):
"""Store scheduled scan configurations"""
FREQUENCY_CHOICES = [
('daily', 'Daily'),
('weekly', 'Weekly'),
('monthly', 'Monthly'),
('quarterly', 'Quarterly'),
]
SCAN_TYPE_CHOICES = [
('full', 'Full Scan'),
('quick', 'Quick Scan'),
('custom', 'Custom Scan'),
]
STATUS_CHOICES = [
('active', 'Active'),
('paused', 'Paused'),
('disabled', 'Disabled'),
]
admin = models.ForeignKey(Administrator, on_delete=models.CASCADE, related_name='scheduled_scans')
name = models.CharField(max_length=200, help_text="Name for this scheduled scan")
domains = models.TextField(help_text="JSON array of domains to scan")
frequency = models.CharField(max_length=20, choices=FREQUENCY_CHOICES, default='weekly')
scan_type = models.CharField(max_length=20, choices=SCAN_TYPE_CHOICES, default='full')
time_of_day = models.TimeField(help_text="Time of day to run the scan (UTC)")
day_of_week = models.IntegerField(null=True, blank=True, help_text="Day of week for weekly scans (0=Monday, 6=Sunday)")
day_of_month = models.IntegerField(null=True, blank=True, help_text="Day of month for monthly scans (1-31)")
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
last_run = models.DateTimeField(null=True, blank=True)
next_run = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# Notification settings
email_notifications = models.BooleanField(default=True)
notification_emails = models.TextField(blank=True, help_text="JSON array of email addresses")
notify_on_threats = models.BooleanField(default=True)
notify_on_completion = models.BooleanField(default=False)
notify_on_failure = models.BooleanField(default=True)
class Meta:
db_table = 'ai_scanner_scheduled_scans'
ordering = ['-created_at']
def __str__(self):
return f"Scheduled Scan: {self.name} ({self.frequency})"
@property
def domain_list(self):
"""Parse domains JSON"""
if self.domains:
try:
return json.loads(self.domains)
except json.JSONDecodeError:
return []
return []
@property
def notification_email_list(self):
"""Parse notification emails JSON"""
if self.notification_emails:
try:
return json.loads(self.notification_emails)
except json.JSONDecodeError:
return []
return []
def set_domains(self, domain_list):
"""Set domains from list"""
self.domains = json.dumps(domain_list)
def set_notification_emails(self, email_list):
"""Set notification emails from list"""
self.notification_emails = json.dumps(email_list)
def calculate_next_run(self):
"""Calculate next run time based on frequency"""
from django.utils import timezone
from datetime import datetime, timedelta
import calendar
now = timezone.now()
if self.frequency == 'daily':
# Daily: next run is tomorrow at specified time
next_run = now.replace(hour=self.time_of_day.hour, minute=self.time_of_day.minute, second=0, microsecond=0)
if next_run <= now:
next_run += timedelta(days=1)
elif self.frequency == 'weekly':
# Weekly: next run is on specified day of week at specified time
days_ahead = self.day_of_week - now.weekday()
if days_ahead <= 0: # Target day already happened this week
days_ahead += 7
next_run = now + timedelta(days=days_ahead)
next_run = next_run.replace(hour=self.time_of_day.hour, minute=self.time_of_day.minute, second=0, microsecond=0)
elif self.frequency == 'monthly':
# Monthly: next run is on specified day of month at specified time
year = now.year
month = now.month
day = min(self.day_of_month, calendar.monthrange(year, month)[1])
next_run = now.replace(day=day, hour=self.time_of_day.hour, minute=self.time_of_day.minute, second=0, microsecond=0)
if next_run <= now:
# Move to next month
if month == 12:
year += 1
month = 1
else:
month += 1
day = min(self.day_of_month, calendar.monthrange(year, month)[1])
next_run = next_run.replace(year=year, month=month, day=day)
elif self.frequency == 'quarterly':
# Quarterly: next run is 3 months from now
next_run = now.replace(hour=self.time_of_day.hour, minute=self.time_of_day.minute, second=0, microsecond=0)
month = now.month
year = now.year
# Add 3 months
month += 3
if month > 12:
year += 1
month -= 12
day = min(self.day_of_month or 1, calendar.monthrange(year, month)[1])
next_run = next_run.replace(year=year, month=month, day=day)
if next_run <= now:
# Add another 3 months
month += 3
if month > 12:
year += 1
month -= 12
day = min(self.day_of_month or 1, calendar.monthrange(year, month)[1])
next_run = next_run.replace(year=year, month=month, day=day)
else:
# Default to weekly
next_run = now + timedelta(weeks=1)
return next_run
def save(self, *args, **kwargs):
"""Override save to calculate next_run"""
if not self.next_run or self.status == 'active':
self.next_run = self.calculate_next_run()
super().save(*args, **kwargs)
class ScheduledScanExecution(models.Model):
"""Track individual executions of scheduled scans"""
STATUS_CHOICES = [
('pending', 'Pending'),
('running', 'Running'),
('completed', 'Completed'),
('failed', 'Failed'),
('cancelled', 'Cancelled'),
]
scheduled_scan = models.ForeignKey(ScheduledScan, on_delete=models.CASCADE, related_name='executions')
execution_time = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
domains_scanned = models.TextField(blank=True, help_text="JSON array of domains that were scanned")
total_scans = models.IntegerField(default=0)
successful_scans = models.IntegerField(default=0)
failed_scans = models.IntegerField(default=0)
total_cost = models.DecimalField(max_digits=10, decimal_places=6, default=0.0)
scan_ids = models.TextField(blank=True, help_text="JSON array of scan IDs created")
error_message = models.TextField(blank=True, null=True)
started_at = models.DateTimeField(null=True, blank=True)
completed_at = models.DateTimeField(null=True, blank=True)
class Meta:
db_table = 'ai_scanner_scheduled_executions'
ordering = ['-execution_time']
def __str__(self):
return f"Execution of {self.scheduled_scan.name} at {self.execution_time}"
@property
def scanned_domains(self):
"""Parse domains scanned JSON"""
if self.domains_scanned:
try:
return json.loads(self.domains_scanned)
except json.JSONDecodeError:
return []
return []
@property
def scan_id_list(self):
"""Parse scan IDs JSON"""
if self.scan_ids:
try:
return json.loads(self.scan_ids)
except json.JSONDecodeError:
return []
return []
def set_scanned_domains(self, domain_list):
"""Set scanned domains from list"""
self.domains_scanned = json.dumps(domain_list)
def set_scan_ids(self, scan_id_list):
"""Set scan IDs from list"""
self.scan_ids = json.dumps(scan_id_list)

View File

@@ -0,0 +1,270 @@
from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from loginSystem.views import loadLoginPage
import json
@require_http_methods(['GET', 'POST'])
def scheduledScans(request):
"""Manage scheduled scans"""
try:
userID = request.session['userID']
from loginSystem.models import Administrator
from .models import ScheduledScan
from plogical.acl import ACLManager
admin = Administrator.objects.get(pk=userID)
currentACL = ACLManager.loadedACL(userID)
if request.method == 'GET':
# Get scheduled scans with ACL respect
if currentACL['admin'] == 1:
# Admin can see all scheduled scans
scheduled_scans = ScheduledScan.objects.all()
else:
# Users can only see their own scheduled scans and their sub-users' scans
user_admins = ACLManager.loadUserObjects(userID)
scheduled_scans = ScheduledScan.objects.filter(admin__in=user_admins)
scan_data = []
for scan in scheduled_scans:
scan_data.append({
'id': scan.id,
'name': scan.name,
'domains': scan.domain_list,
'frequency': scan.frequency,
'scan_type': scan.scan_type,
'time_of_day': scan.time_of_day.strftime('%H:%M'),
'day_of_week': scan.day_of_week,
'day_of_month': scan.day_of_month,
'status': scan.status,
'last_run': scan.last_run.isoformat() if scan.last_run else None,
'next_run': scan.next_run.isoformat() if scan.next_run else None,
'email_notifications': scan.email_notifications,
'notification_emails': scan.notification_email_list,
'notify_on_threats': scan.notify_on_threats,
'notify_on_completion': scan.notify_on_completion,
'notify_on_failure': scan.notify_on_failure,
'created_at': scan.created_at.isoformat()
})
return JsonResponse({'success': True, 'scheduled_scans': scan_data})
elif request.method == 'POST':
# Create new scheduled scan
data = json.loads(request.body)
# Validate required fields
required_fields = ['name', 'domains', 'frequency', 'scan_type', 'time_of_day']
for field in required_fields:
if field not in data or not data[field]:
return JsonResponse({'success': False, 'error': f'Missing required field: {field}'})
# Validate domains
if not isinstance(data['domains'], list) or len(data['domains']) == 0:
return JsonResponse({'success': False, 'error': 'At least one domain must be selected'})
# Check if user has access to these domains
if currentACL['admin'] != 1:
from websiteFunctions.models import Websites
user_domains = set(Websites.objects.filter(admin=admin).values_list('domain', flat=True))
requested_domains = set(data['domains'])
if not requested_domains.issubset(user_domains):
return JsonResponse({'success': False, 'error': 'You do not have access to some of the selected domains'})
# Parse time
from datetime import datetime
try:
time_obj = datetime.strptime(data['time_of_day'], '%H:%M').time()
except ValueError:
return JsonResponse({'success': False, 'error': 'Invalid time format'})
# Create scheduled scan
scheduled_scan = ScheduledScan(
admin=admin,
name=data['name'],
frequency=data['frequency'],
scan_type=data['scan_type'],
time_of_day=time_obj,
email_notifications=data.get('email_notifications', True),
notify_on_threats=data.get('notify_on_threats', True),
notify_on_completion=data.get('notify_on_completion', False),
notify_on_failure=data.get('notify_on_failure', True)
)
# Set domains
scheduled_scan.set_domains(data['domains'])
# Set notification emails
if data.get('notification_emails'):
scheduled_scan.set_notification_emails(data['notification_emails'])
# Set frequency-specific fields
if data['frequency'] == 'weekly' and 'day_of_week' in data:
scheduled_scan.day_of_week = int(data['day_of_week'])
elif data['frequency'] in ['monthly', 'quarterly'] and 'day_of_month' in data:
scheduled_scan.day_of_month = int(data['day_of_month'])
scheduled_scan.save()
return JsonResponse({'success': True, 'id': scheduled_scan.id})
except KeyError:
return JsonResponse({'success': False, 'error': 'Not authenticated'})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@require_http_methods(['GET', 'DELETE'])
def scheduledScanDetail(request, scan_id):
"""Get or delete a specific scheduled scan"""
try:
userID = request.session['userID']
from loginSystem.models import Administrator
from .models import ScheduledScan
from plogical.acl import ACLManager
admin = Administrator.objects.get(pk=userID)
currentACL = ACLManager.loadedACL(userID)
# Get scheduled scan with ACL respect
try:
scheduled_scan = ScheduledScan.objects.get(id=scan_id)
# Check if user has access to this scheduled scan
if currentACL['admin'] != 1:
user_admins = ACLManager.loadUserObjects(userID)
if scheduled_scan.admin not in user_admins:
return JsonResponse({'success': False, 'error': 'Access denied to this scheduled scan'})
except ScheduledScan.DoesNotExist:
return JsonResponse({'success': False, 'error': 'Scheduled scan not found'})
if request.method == 'GET':
# Return scheduled scan details
scan_data = {
'id': scheduled_scan.id,
'name': scheduled_scan.name,
'domains': scheduled_scan.domain_list,
'frequency': scheduled_scan.frequency,
'scan_type': scheduled_scan.scan_type,
'time_of_day': scheduled_scan.time_of_day.strftime('%H:%M'),
'day_of_week': scheduled_scan.day_of_week,
'day_of_month': scheduled_scan.day_of_month,
'status': scheduled_scan.status,
'last_run': scheduled_scan.last_run.isoformat() if scheduled_scan.last_run else None,
'next_run': scheduled_scan.next_run.isoformat() if scheduled_scan.next_run else None,
'email_notifications': scheduled_scan.email_notifications,
'notification_emails': scheduled_scan.notification_email_list,
'notify_on_threats': scheduled_scan.notify_on_threats,
'notify_on_completion': scheduled_scan.notify_on_completion,
'notify_on_failure': scheduled_scan.notify_on_failure,
'created_at': scheduled_scan.created_at.isoformat()
}
return JsonResponse({'success': True, 'scheduled_scan': scan_data})
elif request.method == 'DELETE':
# Delete scheduled scan
scheduled_scan.delete()
return JsonResponse({'success': True})
except KeyError:
return JsonResponse({'success': False, 'error': 'Not authenticated'})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@csrf_exempt
@require_http_methods(['POST'])
def toggleScheduledScan(request, scan_id):
"""Toggle scheduled scan status (active/paused)"""
try:
userID = request.session['userID']
from loginSystem.models import Administrator
from .models import ScheduledScan
from plogical.acl import ACLManager
admin = Administrator.objects.get(pk=userID)
currentACL = ACLManager.loadedACL(userID)
# Get scheduled scan with ACL respect
try:
scheduled_scan = ScheduledScan.objects.get(id=scan_id)
# Check if user has access to this scheduled scan
if currentACL['admin'] != 1:
user_admins = ACLManager.loadUserObjects(userID)
if scheduled_scan.admin not in user_admins:
return JsonResponse({'success': False, 'error': 'Access denied to this scheduled scan'})
except ScheduledScan.DoesNotExist:
return JsonResponse({'success': False, 'error': 'Scheduled scan not found'})
# Toggle status
if scheduled_scan.status == 'active':
scheduled_scan.status = 'paused'
else:
scheduled_scan.status = 'active'
scheduled_scan.save()
return JsonResponse({'success': True, 'status': scheduled_scan.status})
except KeyError:
return JsonResponse({'success': False, 'error': 'Not authenticated'})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
@require_http_methods(['GET'])
def scheduledScanExecutions(request, scan_id):
"""Get execution history for a scheduled scan"""
try:
userID = request.session['userID']
from loginSystem.models import Administrator
from .models import ScheduledScan, ScheduledScanExecution
from plogical.acl import ACLManager
admin = Administrator.objects.get(pk=userID)
currentACL = ACLManager.loadedACL(userID)
# Get scheduled scan with ACL respect
try:
scheduled_scan = ScheduledScan.objects.get(id=scan_id)
# Check if user has access to this scheduled scan
if currentACL['admin'] != 1:
user_admins = ACLManager.loadUserObjects(userID)
if scheduled_scan.admin not in user_admins:
return JsonResponse({'success': False, 'error': 'Access denied to this scheduled scan'})
except ScheduledScan.DoesNotExist:
return JsonResponse({'success': False, 'error': 'Scheduled scan not found'})
# Get execution history
executions = ScheduledScanExecution.objects.filter(scheduled_scan=scheduled_scan).order_by('-execution_time')[:20]
execution_data = []
for execution in executions:
execution_data.append({
'id': execution.id,
'execution_time': execution.execution_time.isoformat(),
'status': execution.status,
'domains_scanned': execution.scanned_domains,
'total_scans': execution.total_scans,
'successful_scans': execution.successful_scans,
'failed_scans': execution.failed_scans,
'total_cost': float(execution.total_cost),
'scan_ids': execution.scan_id_list,
'error_message': execution.error_message,
'started_at': execution.started_at.isoformat() if execution.started_at else None,
'completed_at': execution.completed_at.isoformat() if execution.completed_at else None
})
return JsonResponse({'success': True, 'executions': execution_data})
except KeyError:
return JsonResponse({'success': False, 'error': 'Not authenticated'})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})

View File

@@ -642,6 +642,27 @@ AI Security Scanner - CyberPanel
</div>
</form>
</div>
<!-- Scheduled Scans -->
{% if is_payment_configured or vps_info.is_vps|default:False %}
<div class="scanner-section">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h3 class="scanner-title" style="margin: 0;">
<i class="fas fa-calendar-alt" style="color: #5856d6;"></i>
Scheduled Scans
</h3>
<button class="btn-primary btn-xs" onclick="showScheduleModal()">
<i class="fas fa-plus"></i> Schedule New Scan
</button>
</div>
<div id="scheduledScansContainer">
<!-- Scheduled scans will be loaded here -->
</div>
</div>
{% endif %}
</div>
{% endif %}
<!-- Scan History -->
@@ -1539,5 +1560,427 @@ setInterval(() => {
refreshScanHistory();
}
}, 30000);
// Scheduled Scans Functions
function showScheduleModal() {
$('#scheduleModal').modal('show');
}
function loadScheduledScans() {
fetch('/aiscanner/scheduled-scans/')
.then(response => response.json())
.then(data => {
if (data.success) {
displayScheduledScans(data.scheduled_scans);
} else {
console.error('Failed to load scheduled scans:', data.error);
}
})
.catch(error => {
console.error('Error loading scheduled scans:', error);
});
}
function displayScheduledScans(scans) {
const container = document.getElementById('scheduledScansContainer');
if (!scans || scans.length === 0) {
container.innerHTML = '<p style="color: #94a3b8; text-align: center; padding: 20px;">No scheduled scans configured yet.</p>';
return;
}
let html = '<div class="scheduled-scans-grid">';
scans.forEach(scan => {
const statusClass = scan.status === 'active' ? 'success' : scan.status === 'paused' ? 'warning' : 'danger';
const nextRun = scan.next_run ? new Date(scan.next_run).toLocaleString() : 'Not scheduled';
const lastRun = scan.last_run ? new Date(scan.last_run).toLocaleString() : 'Never';
html += `
<div class="scheduled-scan-card">
<div class="scheduled-scan-header">
<h4>${scan.name}</h4>
<span class="status-label ${statusClass}">${scan.status}</span>
</div>
<div class="scheduled-scan-info">
<p><strong>Frequency:</strong> ${scan.frequency}</p>
<p><strong>Scan Type:</strong> ${scan.scan_type}</p>
<p><strong>Domains:</strong> ${scan.domains.join(', ')}</p>
<p><strong>Next Run:</strong> ${nextRun}</p>
<p><strong>Last Run:</strong> ${lastRun}</p>
</div>
<div class="scheduled-scan-actions">
<button class="btn-xs btn-default" onclick="editScheduledScan(${scan.id})">
<i class="fas fa-edit"></i> Edit
</button>
<button class="btn-xs btn-${scan.status === 'active' ? 'warning' : 'success'}"
onclick="toggleScheduledScan(${scan.id})">
<i class="fas fa-${scan.status === 'active' ? 'pause' : 'play'}"></i>
${scan.status === 'active' ? 'Pause' : 'Activate'}
</button>
<button class="btn-xs btn-danger" onclick="deleteScheduledScan(${scan.id})">
<i class="fas fa-trash"></i> Delete
</button>
</div>
</div>
`;
});
html += '</div>';
container.innerHTML = html;
}
function saveScheduledScan() {
const formData = new FormData(document.getElementById('scheduleForm'));
const data = Object.fromEntries(formData);
// Get selected domains
const selectedDomains = Array.from(document.querySelectorAll('input[name="domains"]:checked'))
.map(cb => cb.value);
if (selectedDomains.length === 0) {
alert('Please select at least one domain to scan.');
return;
}
data.domains = selectedDomains;
// Get notification emails
const notificationEmails = document.getElementById('notificationEmails').value.split(',')
.map(email => email.trim())
.filter(email => email.length > 0);
data.notification_emails = notificationEmails;
fetch('/aiscanner/scheduled-scans/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
if (data.success) {
$('#scheduleModal').modal('hide');
loadScheduledScans();
document.getElementById('scheduleForm').reset();
} else {
alert('Error: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Failed to save scheduled scan. Please try again.');
});
}
function editScheduledScan(id) {
// Fetch scheduled scan details and populate form
fetch(`/aiscanner/scheduled-scans/${id}/`)
.then(response => response.json())
.then(data => {
if (data.success) {
populateScheduleForm(data.scheduled_scan);
$('#scheduleModal').modal('show');
} else {
alert('Error: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Failed to load scheduled scan details.');
});
}
function populateScheduleForm(scan) {
document.getElementById('scheduleId').value = scan.id;
document.getElementById('scheduleName').value = scan.name;
document.getElementById('frequency').value = scan.frequency;
document.getElementById('scanType').value = scan.scan_type;
document.getElementById('timeOfDay').value = scan.time_of_day;
if (scan.day_of_week !== null) {
document.getElementById('dayOfWeek').value = scan.day_of_week;
}
if (scan.day_of_month !== null) {
document.getElementById('dayOfMonth').value = scan.day_of_month;
}
// Select domains
scan.domains.forEach(domain => {
const checkbox = document.querySelector(`input[name="domains"][value="${domain}"]`);
if (checkbox) {
checkbox.checked = true;
}
});
// Set notification settings
document.getElementById('emailNotifications').checked = scan.email_notifications;
document.getElementById('notifyOnThreats').checked = scan.notify_on_threats;
document.getElementById('notifyOnCompletion').checked = scan.notify_on_completion;
document.getElementById('notifyOnFailure').checked = scan.notify_on_failure;
if (scan.notification_emails && scan.notification_emails.length > 0) {
document.getElementById('notificationEmails').value = scan.notification_emails.join(', ');
}
updateFrequencyOptions();
}
function toggleScheduledScan(id) {
fetch(`/aiscanner/scheduled-scans/${id}/toggle/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadScheduledScans();
} else {
alert('Error: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Failed to toggle scheduled scan.');
});
}
function deleteScheduledScan(id) {
if (confirm('Are you sure you want to delete this scheduled scan?')) {
fetch(`/aiscanner/scheduled-scans/${id}/`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadScheduledScans();
} else {
alert('Error: ' + data.error);
}
})
.catch(error => {
console.error('Error:', error);
alert('Failed to delete scheduled scan.');
});
}
}
function updateFrequencyOptions() {
const frequency = document.getElementById('frequency').value;
const dayOfWeekGroup = document.getElementById('dayOfWeekGroup');
const dayOfMonthGroup = document.getElementById('dayOfMonthGroup');
// Hide all optional fields first
dayOfWeekGroup.style.display = 'none';
dayOfMonthGroup.style.display = 'none';
// Show relevant fields based on frequency
if (frequency === 'weekly') {
dayOfWeekGroup.style.display = 'block';
} else if (frequency === 'monthly' || frequency === 'quarterly') {
dayOfMonthGroup.style.display = 'block';
}
}
// Load scheduled scans when page loads
document.addEventListener('DOMContentLoaded', function() {
if (document.getElementById('scheduledScansContainer')) {
loadScheduledScans();
}
});
</script>
<!-- Scheduled Scan Modal -->
<div class="modal fade" id="scheduleModal" tabindex="-1" role="dialog" aria-labelledby="scheduleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="scheduleModalLabel">Schedule New Scan</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form id="scheduleForm">
<div class="modal-body">
<input type="hidden" id="scheduleId" name="id" value="">
<div class="form-group">
<label for="scheduleName">Schedule Name</label>
<input type="text" class="form-control" id="scheduleName" name="name" required>
<small class="form-text text-muted">Give this scheduled scan a descriptive name</small>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="frequency">Frequency</label>
<select class="form-control" id="frequency" name="frequency" onchange="updateFrequencyOptions()" required>
<option value="daily">Daily</option>
<option value="weekly" selected>Weekly</option>
<option value="monthly">Monthly</option>
<option value="quarterly">Quarterly</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="scanType">Scan Type</label>
<select class="form-control" id="scanType" name="scan_type" required>
<option value="full">Full Scan</option>
<option value="quick">Quick Scan</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="timeOfDay">Time of Day (UTC)</label>
<input type="time" class="form-control" id="timeOfDay" name="time_of_day" value="02:00" required>
<small class="form-text text-muted">Time in UTC timezone</small>
</div>
</div>
<div class="col-md-4" id="dayOfWeekGroup" style="display: none;">
<div class="form-group">
<label for="dayOfWeek">Day of Week</label>
<select class="form-control" id="dayOfWeek" name="day_of_week">
<option value="0">Monday</option>
<option value="1">Tuesday</option>
<option value="2">Wednesday</option>
<option value="3">Thursday</option>
<option value="4">Friday</option>
<option value="5">Saturday</option>
<option value="6" selected>Sunday</option>
</select>
</div>
</div>
<div class="col-md-4" id="dayOfMonthGroup" style="display: none;">
<div class="form-group">
<label for="dayOfMonth">Day of Month</label>
<input type="number" class="form-control" id="dayOfMonth" name="day_of_month" min="1" max="31" value="1">
<small class="form-text text-muted">1-31 (will adjust for shorter months)</small>
</div>
</div>
</div>
<div class="form-group">
<label>Websites to Scan</label>
<div class="checkbox-group" style="max-height: 200px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; border-radius: 4px;">
{% for website in websites %}
<div class="form-check">
<input class="form-check-input" type="checkbox" name="domains" value="{{ website.domain }}" id="domain_{{ forloop.counter }}">
<label class="form-check-label" for="domain_{{ forloop.counter }}">
{{ website.domain }}
</label>
</div>
{% endfor %}
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="emailNotifications" name="email_notifications" checked>
<label class="form-check-label" for="emailNotifications">
Enable Email Notifications
</label>
</div>
</div>
<div class="form-group">
<label for="notificationEmails">Notification Email Addresses</label>
<input type="text" class="form-control" id="notificationEmails" name="notification_emails" placeholder="email1@example.com, email2@example.com">
<small class="form-text text-muted">Separate multiple emails with commas. Leave empty to use your account email.</small>
</div>
<div class="form-group">
<label>Notification Preferences</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="notifyOnThreats" name="notify_on_threats" checked>
<label class="form-check-label" for="notifyOnThreats">
Notify when threats are found
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="notifyOnCompletion" name="notify_on_completion">
<label class="form-check-label" for="notifyOnCompletion">
Notify when scan completes successfully
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="notifyOnFailure" name="notify_on_failure" checked>
<label class="form-check-label" for="notifyOnFailure">
Notify when scan fails
</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="saveScheduledScan()">Save Schedule</button>
</div>
</form>
</div>
</div>
</div>
<style>
.scheduled-scans-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
}
.scheduled-scan-card {
border: 1px solid #e8e9ff;
border-radius: 8px;
padding: 20px;
background: white;
}
.scheduled-scan-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.scheduled-scan-header h4 {
margin: 0;
color: #2f3640;
font-size: 16px;
}
.scheduled-scan-info p {
margin: 5px 0;
color: #64748b;
font-size: 14px;
}
.scheduled-scan-actions {
display: flex;
gap: 10px;
margin-top: 15px;
flex-wrap: wrap;
}
.checkbox-group {
background: #f8f9fa;
}
.form-check {
margin-bottom: 5px;
}
</style>
{% endblock %}

View File

@@ -1,5 +1,5 @@
from django.urls import path
from . import views, api
from . import views, api, scheduled_views
urlpatterns = [
# Main AI Scanner pages
@@ -18,6 +18,12 @@ urlpatterns = [
path('platform-monitor-url/<str:scan_id>/', views.getPlatformMonitorUrl, name='aiScannerPlatformMonitorUrl'),
path('platform-status/<str:scan_id>/', views.getPlatformScanStatus, name='aiScannerPlatformStatus'),
# Scheduled scans management
path('scheduled-scans/', scheduled_views.scheduledScans, name='aiScannerScheduledScans'),
path('scheduled-scans/<int:scan_id>/', scheduled_views.scheduledScanDetail, name='aiScannerScheduledScanDetail'),
path('scheduled-scans/<int:scan_id>/toggle/', scheduled_views.toggleScheduledScan, name='aiScannerToggleScheduledScan'),
path('scheduled-scans/<int:scan_id>/executions/', scheduled_views.scheduledScanExecutions, name='aiScannerScheduledScanExecutions'),
# Note: RESTful API endpoints are in /api/urls.py for external access
# Legacy API endpoints (for backward compatibility)

View File

@@ -894,6 +894,63 @@ $cfg['Servers'][$i]['LogoutURL'] = 'phpmyadminsignin.php?logout';
except:
pass
# AI Scanner Scheduled Scans Tables
try:
cursor.execute('''
CREATE TABLE `ai_scanner_scheduled_scans` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`admin_id` integer NOT NULL,
`name` varchar(200) NOT NULL,
`domains` longtext NOT NULL,
`frequency` varchar(20) NOT NULL DEFAULT 'weekly',
`scan_type` varchar(20) NOT NULL DEFAULT 'full',
`time_of_day` time NOT NULL,
`day_of_week` integer DEFAULT NULL,
`day_of_month` integer DEFAULT NULL,
`status` varchar(20) NOT NULL DEFAULT 'active',
`last_run` datetime(6) DEFAULT NULL,
`next_run` datetime(6) DEFAULT NULL,
`created_at` datetime(6) NOT NULL,
`updated_at` datetime(6) NOT NULL,
`email_notifications` bool NOT NULL DEFAULT 1,
`notification_emails` longtext NOT NULL DEFAULT '',
`notify_on_threats` bool NOT NULL DEFAULT 1,
`notify_on_completion` bool NOT NULL DEFAULT 0,
`notify_on_failure` bool NOT NULL DEFAULT 1,
KEY `ai_scanner_scheduled_scans_admin_id_idx` (`admin_id`),
KEY `ai_scanner_scheduled_scans_status_next_run_idx` (`status`, `next_run`),
CONSTRAINT `ai_scanner_scheduled_scans_admin_id_fk` FOREIGN KEY (`admin_id`)
REFERENCES `loginSystem_administrator` (`id`) ON DELETE CASCADE
)
''')
except:
pass
try:
cursor.execute('''
CREATE TABLE `ai_scanner_scheduled_executions` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`scheduled_scan_id` integer NOT NULL,
`execution_time` datetime(6) NOT NULL,
`status` varchar(20) NOT NULL DEFAULT 'pending',
`domains_scanned` longtext NOT NULL DEFAULT '',
`total_scans` integer NOT NULL DEFAULT 0,
`successful_scans` integer NOT NULL DEFAULT 0,
`failed_scans` integer NOT NULL DEFAULT 0,
`total_cost` decimal(10,6) NOT NULL DEFAULT 0.000000,
`scan_ids` longtext NOT NULL DEFAULT '',
`error_message` longtext DEFAULT NULL,
`started_at` datetime(6) DEFAULT NULL,
`completed_at` datetime(6) DEFAULT NULL,
KEY `ai_scanner_scheduled_executions_scheduled_scan_id_idx` (`scheduled_scan_id`),
KEY `ai_scanner_scheduled_executions_execution_time_idx` (`execution_time` DESC),
CONSTRAINT `ai_scanner_scheduled_executions_scheduled_scan_id_fk` FOREIGN KEY (`scheduled_scan_id`)
REFERENCES `ai_scanner_scheduled_scans` (`id`) ON DELETE CASCADE
)
''')
except:
pass
try:
cursor.execute(
'CREATE TABLE `loginSystem_acl` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(50) NOT NULL UNIQUE, `adminStatus` integer NOT NULL DEFAULT 0, `versionManagement` integer NOT NULL DEFAULT 0, `createNewUser` integer NOT NULL DEFAULT 0, `deleteUser` integer NOT NULL DEFAULT 0, `resellerCenter` integer NOT NULL DEFAULT 0, `changeUserACL` integer NOT NULL DEFAULT 0, `createWebsite` integer NOT NULL DEFAULT 0, `modifyWebsite` integer NOT NULL DEFAULT 0, `suspendWebsite` integer NOT NULL DEFAULT 0, `deleteWebsite` integer NOT NULL DEFAULT 0, `createPackage` integer NOT NULL DEFAULT 0, `deletePackage` integer NOT NULL DEFAULT 0, `modifyPackage` integer NOT NULL DEFAULT 0, `createDatabase` integer NOT NULL DEFAULT 0, `deleteDatabase` integer NOT NULL DEFAULT 0, `listDatabases` integer NOT NULL DEFAULT 0, `createNameServer` integer NOT NULL DEFAULT 0, `createDNSZone` integer NOT NULL DEFAULT 0, `deleteZone` integer NOT NULL DEFAULT 0, `addDeleteRecords` integer NOT NULL DEFAULT 0, `createEmail` integer NOT NULL DEFAULT 0, `deleteEmail` integer NOT NULL DEFAULT 0, `emailForwarding` integer NOT NULL DEFAULT 0, `changeEmailPassword` integer NOT NULL DEFAULT 0, `dkimManager` integer NOT NULL DEFAULT 0, `createFTPAccount` integer NOT NULL DEFAULT 0, `deleteFTPAccount` integer NOT NULL DEFAULT 0, `listFTPAccounts` integer NOT NULL DEFAULT 0, `createBackup` integer NOT NULL DEFAULT 0, `restoreBackup` integer NOT NULL DEFAULT 0, `addDeleteDestinations` integer NOT NULL DEFAULT 0, `scheduleBackups` integer NOT NULL DEFAULT 0, `remoteBackups` integer NOT NULL DEFAULT 0, `manageSSL` integer NOT NULL DEFAULT 0, `hostnameSSL` integer NOT NULL DEFAULT 0, `mailServerSSL` integer NOT NULL DEFAULT 0)')
@@ -3076,6 +3133,13 @@ vmail
command = """sed -i '/CyberCP/d' /etc/crontab"""
Upgrade.executioner(command, command, 0, True)
# Ensure log directory exists for scheduled scans
if not os.path.exists('/usr/local/lscp/logs'):
try:
os.makedirs('/usr/local/lscp/logs', mode=0o755)
except:
pass
if os.path.exists('/usr/local/lsws/conf/httpd.conf'):
# Setup /usr/local/lsws/conf/httpd.conf to use new Logformat standard for better stats and accesslogs
command = """sed -i "s|^LogFormat.*|LogFormat '%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"' combined|g" /usr/local/lsws/conf/httpd.conf"""
@@ -3109,6 +3173,7 @@ vmail
0 0 * * 4 /usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/renew.py >/dev/null 2>&1
7 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
*/3 * * * * if ! find /home/*/public_html/ -maxdepth 2 -type f -newer /usr/local/lsws/cgid -name '.htaccess' -exec false {} +; then /usr/local/lsws/bin/lswsctrl restart; fi
* * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/manage.py run_scheduled_scans >/usr/local/lscp/logs/scheduled_scans.log 2>&1
"""
writeToFile = open(cronPath, 'w')
@@ -3133,6 +3198,15 @@ vmail
0 1 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '1 Day'
0 0 */3 * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '3 Days'
0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py '1 Week'
"""
writeToFile = open(cronPath, 'a')
writeToFile.write(content)
writeToFile.close()
# Add AI Scanner scheduled scans cron job if missing
if data.find('run_scheduled_scans') == -1:
content = """
* * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/manage.py run_scheduled_scans >/usr/local/lscp/logs/scheduled_scans.log 2>&1
"""
writeToFile = open(cronPath, 'a')
writeToFile.write(content)
@@ -3149,6 +3223,7 @@ vmail
7 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
0 0 * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Daily
0 0 * * 0 /usr/local/CyberCP/bin/python /usr/local/CyberCP/IncBackups/IncScheduler.py Weekly
* * * * * /usr/local/CyberCP/bin/python /usr/local/CyberCP/manage.py run_scheduled_scans >/usr/local/lscp/logs/scheduled_scans.log 2>&1
"""
writeToFile = open(cronPath, 'w')
writeToFile.write(content)