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()
|
||||
|
||||
# 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
|
||||
if scan_record.cost_usd > 0:
|
||||
try:
|
||||
|
||||
@@ -718,9 +718,16 @@ AI Security Scanner - CyberPanel
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-default btn-xs" onclick="viewScanDetails('{{ scan.scan_id }}')">
|
||||
<i class="fas fa-eye"></i> View
|
||||
</button>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
<button class="btn-default btn-xs" onclick="viewScanDetails('{{ scan.scan_id }}')">
|
||||
<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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -953,9 +960,16 @@ function updateScanHistoryTable(scans) {
|
||||
`<span style="color: #ef4444; font-weight: 700;">${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}')">
|
||||
<i class="fas fa-eye"></i> View
|
||||
</button>`;
|
||||
const actionsHtml = `<div style="display: flex; gap: 5px;">
|
||||
<button class="btn-default btn-xs" onclick="viewScanDetails('${scan.scan_id}')">
|
||||
<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') ?
|
||||
`<div class="progress" style="height: 20px;">
|
||||
@@ -1011,6 +1025,24 @@ function viewScanDetails(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) {
|
||||
console.log(`[AI Scanner] Fetching live progress for scan: ${scanId}`);
|
||||
fetch(`/api/ai-scanner/scan/${scanId}/live-progress`)
|
||||
@@ -1207,21 +1239,29 @@ function displayCompletedScanDetails(scan) {
|
||||
</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 = `
|
||||
<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-icon info" style="width: 40px; height: 40px; font-size: 18px; margin-bottom: 10px;">
|
||||
<i class="fas fa-file"></i>
|
||||
</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>
|
||||
</div>
|
||||
<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;">
|
||||
<i class="fas ${scan.issues_found > 0 ? 'fa-exclamation-triangle' : 'fa-check'}"></i>
|
||||
<div class="stat-card-icon ${threatsFound > 0 ? 'danger' : 'success'}" style="width: 40px; height: 40px; font-size: 18px; margin-bottom: 10px;">
|
||||
<i class="fas ${threatsFound > 0 ? 'fa-exclamation-triangle' : 'fa-check'}"></i>
|
||||
</div>
|
||||
<h4 style="margin: 0; font-size: 24px; font-weight: 700;">${scan.issues_found || 0}</h4>
|
||||
<p style="margin: 5px 0 0 0; font-size: 13px;">Issues Found</p>
|
||||
<h4 style="margin: 0; font-size: 24px; font-weight: 700;">${threatsFound}</h4>
|
||||
<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 class="stat-card" style="padding: 20px;">
|
||||
<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>
|
||||
${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
|
||||
path('scan-history/', views.getScanHistory, name='aiScannerHistory'),
|
||||
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'),
|
||||
|
||||
# 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)})
|
||||
|
||||
|
||||
@require_http_methods(['GET'])
|
||||
def getScanDetails(request, scan_id):
|
||||
"""Get detailed scan results"""
|
||||
try:
|
||||
userID = request.session['userID']
|
||||
from loginSystem.models import Administrator
|
||||
from .models import ScanHistory
|
||||
from .status_models import ScanStatusUpdate
|
||||
from plogical.acl import ACLManager
|
||||
|
||||
admin = Administrator.objects.get(pk=userID)
|
||||
@@ -153,6 +155,23 @@ def getScanDetails(request, scan_id):
|
||||
except ScanHistory.DoesNotExist:
|
||||
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_id': scan.scan_id,
|
||||
'domain': scan.domain,
|
||||
@@ -161,8 +180,12 @@ def getScanDetails(request, scan_id):
|
||||
'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,
|
||||
'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,
|
||||
'threats_found': threats_found,
|
||||
'critical_threats': critical_threats,
|
||||
'high_threats': high_threats,
|
||||
'findings': scan.findings,
|
||||
'summary': scan.summary,
|
||||
'error_message': scan.error_message
|
||||
@@ -178,6 +201,86 @@ def getScanDetails(request, scan_id):
|
||||
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):
|
||||
"""Get real-time scan status from AI Scanner platform"""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user