mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-18 03:01:01 +01:00
address some issues in aiscanner
This commit is contained in:
@@ -560,6 +560,33 @@ def scan_callback(request):
|
|||||||
|
|
||||||
scan_record.save()
|
scan_record.save()
|
||||||
|
|
||||||
|
# Also update the ScanStatusUpdate record with final statistics
|
||||||
|
try:
|
||||||
|
from .status_models import ScanStatusUpdate
|
||||||
|
status_update, _ = ScanStatusUpdate.objects.get_or_create(scan_id=scan_id)
|
||||||
|
status_update.phase = 'completed'
|
||||||
|
status_update.progress = 100
|
||||||
|
status_update.files_discovered = summary.get('files_scanned', 0) # Use files_scanned as approximation
|
||||||
|
status_update.files_scanned = summary.get('files_scanned', 0)
|
||||||
|
status_update.files_remaining = 0
|
||||||
|
status_update.threats_found = summary.get('total_findings', 0)
|
||||||
|
# Extract critical and high threats from findings if available
|
||||||
|
critical_count = 0
|
||||||
|
high_count = 0
|
||||||
|
for finding in findings:
|
||||||
|
severity = finding.get('severity', '').lower()
|
||||||
|
if severity == 'critical':
|
||||||
|
critical_count += 1
|
||||||
|
elif severity == 'high':
|
||||||
|
high_count += 1
|
||||||
|
status_update.critical_threats = critical_count
|
||||||
|
status_update.high_threats = high_count
|
||||||
|
status_update.activity_description = f"Scan completed - {summary.get('total_findings', 0)} threats found"
|
||||||
|
status_update.save()
|
||||||
|
logging.writeToFile(f"[API] Updated ScanStatusUpdate for completed scan {scan_id}")
|
||||||
|
except Exception as e:
|
||||||
|
logging.writeToFile(f"[API] Error updating ScanStatusUpdate: {str(e)}")
|
||||||
|
|
||||||
# Update user balance if scan cost money
|
# Update user balance if scan cost money
|
||||||
if scan_record.cost_usd > 0:
|
if scan_record.cost_usd > 0:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -718,9 +718,16 @@ AI Security Scanner - CyberPanel
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn-default btn-xs" onclick="viewScanDetails('{{ scan.scan_id }}')">
|
<div style="display: flex; gap: 5px;">
|
||||||
<i class="fas fa-eye"></i> View
|
<button class="btn-default btn-xs" onclick="viewScanDetails('{{ scan.scan_id }}')">
|
||||||
</button>
|
<i class="fas fa-eye"></i> View
|
||||||
|
</button>
|
||||||
|
{% if scan.status == 'completed' %}
|
||||||
|
<button class="btn-primary btn-xs" onclick="viewOnPlatform('{{ scan.scan_id }}')" title="View detailed AI analysis on platform">
|
||||||
|
<i class="fas fa-external-link-alt"></i> AI Analysis
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -953,9 +960,16 @@ function updateScanHistoryTable(scans) {
|
|||||||
`<span style="color: #ef4444; font-weight: 700;">${scan.issues_found}</span>` :
|
`<span style="color: #ef4444; font-weight: 700;">${scan.issues_found}</span>` :
|
||||||
`<span style="color: #10b981;">${scan.issues_found}</span>`;
|
`<span style="color: #10b981;">${scan.issues_found}</span>`;
|
||||||
|
|
||||||
const actionsHtml = `<button class="btn-default btn-xs" onclick="viewScanDetails('${scan.scan_id}')">
|
const actionsHtml = `<div style="display: flex; gap: 5px;">
|
||||||
<i class="fas fa-eye"></i> View
|
<button class="btn-default btn-xs" onclick="viewScanDetails('${scan.scan_id}')">
|
||||||
</button>`;
|
<i class="fas fa-eye"></i> View
|
||||||
|
</button>
|
||||||
|
${scan.status === 'completed' ? `
|
||||||
|
<button class="btn-primary btn-xs" onclick="viewOnPlatform('${scan.scan_id}')" title="View detailed AI analysis on platform">
|
||||||
|
<i class="fas fa-external-link-alt"></i> AI Analysis
|
||||||
|
</button>
|
||||||
|
` : ''}
|
||||||
|
</div>`;
|
||||||
|
|
||||||
const progressHtml = (scan.status === 'running' || scan.status === 'pending') ?
|
const progressHtml = (scan.status === 'running' || scan.status === 'pending') ?
|
||||||
`<div class="progress" style="height: 20px;">
|
`<div class="progress" style="height: 20px;">
|
||||||
@@ -1011,6 +1025,24 @@ function viewScanDetails(scanId) {
|
|||||||
fetchLiveProgress(scanId);
|
fetchLiveProgress(scanId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function viewOnPlatform(scanId) {
|
||||||
|
// Fetch the platform monitor URL
|
||||||
|
fetch(`/aiscanner/platform-monitor-url/${scanId}/`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.monitor_url) {
|
||||||
|
// Open in new tab
|
||||||
|
window.open(data.monitor_url, '_blank');
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + (data.error || 'Could not get platform URL'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
alert('Failed to get platform URL. Please try again.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function fetchLiveProgress(scanId) {
|
function fetchLiveProgress(scanId) {
|
||||||
console.log(`[AI Scanner] Fetching live progress for scan: ${scanId}`);
|
console.log(`[AI Scanner] Fetching live progress for scan: ${scanId}`);
|
||||||
fetch(`/api/ai-scanner/scan/${scanId}/live-progress`)
|
fetch(`/api/ai-scanner/scan/${scanId}/live-progress`)
|
||||||
@@ -1207,21 +1239,29 @@ function displayCompletedScanDetails(scan) {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use enhanced scan data from ScanStatusUpdate if available
|
||||||
|
const filesScanned = scan.files_scanned || 0;
|
||||||
|
const filesDiscovered = scan.files_discovered || scan.files_scanned || 0;
|
||||||
|
const threatsFound = scan.threats_found !== undefined ? scan.threats_found : (scan.issues_found || 0);
|
||||||
|
const criticalThreats = scan.critical_threats || 0;
|
||||||
|
const highThreats = scan.high_threats || 0;
|
||||||
|
|
||||||
document.getElementById('completedSection').innerHTML = `
|
document.getElementById('completedSection').innerHTML = `
|
||||||
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 20px;">
|
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 20px;">
|
||||||
<div class="stat-card" style="padding: 20px;">
|
<div class="stat-card" style="padding: 20px;">
|
||||||
<div class="stat-card-icon info" style="width: 40px; height: 40px; font-size: 18px; margin-bottom: 10px;">
|
<div class="stat-card-icon info" style="width: 40px; height: 40px; font-size: 18px; margin-bottom: 10px;">
|
||||||
<i class="fas fa-file"></i>
|
<i class="fas fa-file"></i>
|
||||||
</div>
|
</div>
|
||||||
<h4 style="margin: 0; font-size: 24px; font-weight: 700;">${scan.files_scanned || 0}</h4>
|
<h4 style="margin: 0; font-size: 24px; font-weight: 700;">${filesScanned}${filesDiscovered > filesScanned ? `/${filesDiscovered}` : ''}</h4>
|
||||||
<p style="margin: 5px 0 0 0; font-size: 13px;">Files Scanned</p>
|
<p style="margin: 5px 0 0 0; font-size: 13px;">Files Scanned</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card" style="padding: 20px;">
|
<div class="stat-card" style="padding: 20px;">
|
||||||
<div class="stat-card-icon ${scan.issues_found > 0 ? 'danger' : 'success'}" style="width: 40px; height: 40px; font-size: 18px; margin-bottom: 10px;">
|
<div class="stat-card-icon ${threatsFound > 0 ? 'danger' : 'success'}" style="width: 40px; height: 40px; font-size: 18px; margin-bottom: 10px;">
|
||||||
<i class="fas ${scan.issues_found > 0 ? 'fa-exclamation-triangle' : 'fa-check'}"></i>
|
<i class="fas ${threatsFound > 0 ? 'fa-exclamation-triangle' : 'fa-check'}"></i>
|
||||||
</div>
|
</div>
|
||||||
<h4 style="margin: 0; font-size: 24px; font-weight: 700;">${scan.issues_found || 0}</h4>
|
<h4 style="margin: 0; font-size: 24px; font-weight: 700;">${threatsFound}</h4>
|
||||||
<p style="margin: 5px 0 0 0; font-size: 13px;">Issues Found</p>
|
<p style="margin: 5px 0 0 0; font-size: 13px;">Threats Found</p>
|
||||||
|
${(criticalThreats > 0 || highThreats > 0) ? `<small style="color: #ef4444; font-size: 11px;">${criticalThreats > 0 ? `${criticalThreats} Critical` : ''}${criticalThreats > 0 && highThreats > 0 ? ', ' : ''}${highThreats > 0 ? `${highThreats} High` : ''}</small>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-card" style="padding: 20px;">
|
<div class="stat-card" style="padding: 20px;">
|
||||||
<div class="stat-card-icon info" style="width: 40px; height: 40px; font-size: 18px; margin-bottom: 10px;">
|
<div class="stat-card-icon info" style="width: 40px; height: 40px; font-size: 18px; margin-bottom: 10px;">
|
||||||
@@ -1239,6 +1279,11 @@ function displayCompletedScanDetails(scan) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${findingsHtml}
|
${findingsHtml}
|
||||||
|
<div style="margin-top: 20px; text-align: center;">
|
||||||
|
<button class="btn-primary" onclick="viewOnPlatform('${scan.scan_id}')" style="padding: 0.75rem 2rem;">
|
||||||
|
<i class="fas fa-external-link-alt"></i> View Full AI Analysis on Platform
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ urlpatterns = [
|
|||||||
# Scan management
|
# Scan management
|
||||||
path('scan-history/', views.getScanHistory, name='aiScannerHistory'),
|
path('scan-history/', views.getScanHistory, name='aiScannerHistory'),
|
||||||
path('scan-details/<str:scan_id>/', views.getScanDetails, name='aiScannerDetails'),
|
path('scan-details/<str:scan_id>/', views.getScanDetails, name='aiScannerDetails'),
|
||||||
|
path('platform-monitor-url/<str:scan_id>/', views.getPlatformMonitorUrl, name='aiScannerPlatformMonitorUrl'),
|
||||||
path('platform-status/<str:scan_id>/', views.getPlatformScanStatus, name='aiScannerPlatformStatus'),
|
path('platform-status/<str:scan_id>/', views.getPlatformScanStatus, name='aiScannerPlatformStatus'),
|
||||||
|
|
||||||
# Note: RESTful API endpoints are in /api/urls.py for external access
|
# Note: RESTful API endpoints are in /api/urls.py for external access
|
||||||
|
|||||||
@@ -129,12 +129,14 @@ def getScanHistory(request):
|
|||||||
return JsonResponse({'success': False, 'error': str(e)})
|
return JsonResponse({'success': False, 'error': str(e)})
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(['GET'])
|
||||||
def getScanDetails(request, scan_id):
|
def getScanDetails(request, scan_id):
|
||||||
"""Get detailed scan results"""
|
"""Get detailed scan results"""
|
||||||
try:
|
try:
|
||||||
userID = request.session['userID']
|
userID = request.session['userID']
|
||||||
from loginSystem.models import Administrator
|
from loginSystem.models import Administrator
|
||||||
from .models import ScanHistory
|
from .models import ScanHistory
|
||||||
|
from .status_models import ScanStatusUpdate
|
||||||
from plogical.acl import ACLManager
|
from plogical.acl import ACLManager
|
||||||
|
|
||||||
admin = Administrator.objects.get(pk=userID)
|
admin = Administrator.objects.get(pk=userID)
|
||||||
@@ -153,6 +155,23 @@ def getScanDetails(request, scan_id):
|
|||||||
except ScanHistory.DoesNotExist:
|
except ScanHistory.DoesNotExist:
|
||||||
return JsonResponse({'success': False, 'error': 'Scan not found'})
|
return JsonResponse({'success': False, 'error': 'Scan not found'})
|
||||||
|
|
||||||
|
# Get the status update for more detailed information
|
||||||
|
try:
|
||||||
|
status_update = ScanStatusUpdate.objects.get(scan_id=scan_id)
|
||||||
|
# Use detailed information from status update if available
|
||||||
|
files_scanned = status_update.files_scanned if status_update.files_scanned > 0 else scan.files_scanned
|
||||||
|
files_discovered = status_update.files_discovered
|
||||||
|
threats_found = status_update.threats_found
|
||||||
|
critical_threats = status_update.critical_threats
|
||||||
|
high_threats = status_update.high_threats
|
||||||
|
except ScanStatusUpdate.DoesNotExist:
|
||||||
|
# Fall back to basic information from scan history
|
||||||
|
files_scanned = scan.files_scanned
|
||||||
|
files_discovered = scan.files_scanned # Approximate
|
||||||
|
threats_found = scan.issues_found
|
||||||
|
critical_threats = 0
|
||||||
|
high_threats = 0
|
||||||
|
|
||||||
scan_data = {
|
scan_data = {
|
||||||
'scan_id': scan.scan_id,
|
'scan_id': scan.scan_id,
|
||||||
'domain': scan.domain,
|
'domain': scan.domain,
|
||||||
@@ -161,8 +180,12 @@ def getScanDetails(request, scan_id):
|
|||||||
'started_at': scan.started_at.strftime('%Y-%m-%d %H:%M:%S'),
|
'started_at': scan.started_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
'completed_at': scan.completed_at.strftime('%Y-%m-%d %H:%M:%S') if scan.completed_at else None,
|
'completed_at': scan.completed_at.strftime('%Y-%m-%d %H:%M:%S') if scan.completed_at else None,
|
||||||
'cost_usd': float(scan.cost_usd) if scan.cost_usd else 0,
|
'cost_usd': float(scan.cost_usd) if scan.cost_usd else 0,
|
||||||
'files_scanned': scan.files_scanned,
|
'files_scanned': files_scanned,
|
||||||
|
'files_discovered': files_discovered,
|
||||||
'issues_found': scan.issues_found,
|
'issues_found': scan.issues_found,
|
||||||
|
'threats_found': threats_found,
|
||||||
|
'critical_threats': critical_threats,
|
||||||
|
'high_threats': high_threats,
|
||||||
'findings': scan.findings,
|
'findings': scan.findings,
|
||||||
'summary': scan.summary,
|
'summary': scan.summary,
|
||||||
'error_message': scan.error_message
|
'error_message': scan.error_message
|
||||||
@@ -178,6 +201,86 @@ def getScanDetails(request, scan_id):
|
|||||||
return JsonResponse({'success': False, 'error': str(e)})
|
return JsonResponse({'success': False, 'error': str(e)})
|
||||||
|
|
||||||
|
|
||||||
|
@require_http_methods(['GET'])
|
||||||
|
def getPlatformMonitorUrl(request, scan_id):
|
||||||
|
"""Get the platform monitor URL for a scan"""
|
||||||
|
try:
|
||||||
|
userID = request.session['userID']
|
||||||
|
from loginSystem.models import Administrator
|
||||||
|
from .models import ScanHistory, AIScannerSettings
|
||||||
|
from plogical.CyberCPLogFileWriter import CyberCPLogFileWriter as logging
|
||||||
|
import requests
|
||||||
|
|
||||||
|
# Get scan to verify ownership
|
||||||
|
try:
|
||||||
|
scan = ScanHistory.objects.get(scan_id=scan_id)
|
||||||
|
admin = Administrator.objects.get(pk=userID)
|
||||||
|
|
||||||
|
# Verify access
|
||||||
|
from plogical.acl import ACLManager
|
||||||
|
currentACL = ACLManager.loadedACL(userID)
|
||||||
|
if currentACL['admin'] != 1:
|
||||||
|
user_admins = ACLManager.loadUserObjects(userID)
|
||||||
|
if scan.admin not in user_admins:
|
||||||
|
return JsonResponse({'success': False, 'error': 'Access denied'})
|
||||||
|
except ScanHistory.DoesNotExist:
|
||||||
|
return JsonResponse({'success': False, 'error': 'Scan not found'})
|
||||||
|
|
||||||
|
# Get API key for the scan owner
|
||||||
|
try:
|
||||||
|
scanner_settings = scan.admin.ai_scanner_settings
|
||||||
|
if not scanner_settings.api_key:
|
||||||
|
return JsonResponse({'success': False, 'error': 'API key not configured'})
|
||||||
|
api_key = scanner_settings.api_key
|
||||||
|
except:
|
||||||
|
return JsonResponse({'success': False, 'error': 'Scanner not configured'})
|
||||||
|
|
||||||
|
# Call platform API to get monitor URL
|
||||||
|
try:
|
||||||
|
url = f"https://platform.cyberpersons.com/ai-scanner/api/ai-scanner/scan/{scan_id}/monitor-url/"
|
||||||
|
headers = {
|
||||||
|
'Authorization': f'Bearer {api_key}',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.writeToFile(f"[AI Scanner] Fetching platform monitor URL for scan {scan_id}")
|
||||||
|
|
||||||
|
response = requests.get(url, headers=headers, timeout=10)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if response.status_code == 200 and data.get('success'):
|
||||||
|
logging.writeToFile(f"[AI Scanner] Got monitor URL: {data.get('monitor_url')}")
|
||||||
|
return JsonResponse({
|
||||||
|
'success': True,
|
||||||
|
'monitor_url': data.get('monitor_url'),
|
||||||
|
'platform_scan_id': data.get('platform_scan_id')
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
error_msg = data.get('error', 'Failed to get monitor URL')
|
||||||
|
logging.writeToFile(f"[AI Scanner] Failed to get monitor URL: {error_msg}")
|
||||||
|
return JsonResponse({
|
||||||
|
'success': False,
|
||||||
|
'error': error_msg,
|
||||||
|
'scan_exists': data.get('scan_exists', False)
|
||||||
|
})
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
logging.writeToFile(f"[AI Scanner] Platform request timeout for scan {scan_id}")
|
||||||
|
return JsonResponse({'success': False, 'error': 'Platform request timeout'})
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logging.writeToFile(f"[AI Scanner] Platform request error: {str(e)}")
|
||||||
|
return JsonResponse({'success': False, 'error': f'Platform error: {str(e)}'})
|
||||||
|
except Exception as e:
|
||||||
|
logging.writeToFile(f"[AI Scanner] Unexpected error: {str(e)}")
|
||||||
|
return JsonResponse({'success': False, 'error': f'Error: {str(e)}'})
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
return JsonResponse({'success': False, 'error': 'Not authenticated'})
|
||||||
|
except Exception as e:
|
||||||
|
logging.writeToFile(f"[AI Scanner] getPlatformMonitorUrl error: {str(e)}")
|
||||||
|
return JsonResponse({'success': False, 'error': str(e)})
|
||||||
|
|
||||||
|
|
||||||
def getPlatformScanStatus(request, scan_id):
|
def getPlatformScanStatus(request, scan_id):
|
||||||
"""Get real-time scan status from AI Scanner platform"""
|
"""Get real-time scan status from AI Scanner platform"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user