Improved Summery Section

This commit is contained in:
BlackSnufkin
2025-01-05 03:04:30 -08:00
parent 867a0a0529
commit 48fa4cfbe9
7 changed files with 657 additions and 193 deletions
+299 -44
View File
@@ -324,6 +324,156 @@ def validate_pid(pid):
return False, f"Error validating PID: {str(e)}"
def get_entropy_risk_level(entropy):
if entropy > 7.2:
return 'High'
elif entropy > 6.8:
return 'Medium'
return 'Low'
def format_hex(value):
if isinstance(value, str) and value.startswith('0x'):
return value.lower()
try:
return f"0x{int(value):x}"
except (ValueError, TypeError):
return str(value)
def calculate_file_risk(file_info, static_results=None, dynamic_results=None):
"""
Calculate overall file risk score based on all available analysis results.
Returns a tuple of (risk_score, risk_factors) where:
- risk_score is a float between 0 and 100
- risk_factors is a list of contributing risk factors
"""
risk_score = 0
risk_factors = []
# Base weights for different analysis types
WEIGHTS = {
'pe_info': 0.3,
'static': 0.3,
'dynamic': 0.4
}
# 1. PE Information Risk Calculation
if file_info.get('pe_info'):
pe_risk = 0
pe_info = file_info['pe_info']
# Check section entropy
high_entropy_sections = 0
for section in pe_info.get('sections', []):
if section.get('entropy', 0) > 7.2: # High risk threshold
high_entropy_sections += 1
risk_factors.append(f"High entropy in section {section.get('name', 'UNKNOWN')}")
pe_risk += min(high_entropy_sections * 20, 40) # Cap at 40 points
# Check suspicious imports
suspicious_imports = len(pe_info.get('suspicious_imports', []))
if suspicious_imports > 0:
pe_risk += min(suspicious_imports * 10, 30) # Cap at 30 points
risk_factors.append(f"Found {suspicious_imports} suspicious imports")
# Check checksum mismatch
if pe_info.get('checksum_info'):
checksum = pe_info['checksum_info']
if checksum.get('stored_checksum') != checksum.get('calculated_checksum'):
pe_risk += 30
risk_factors.append("PE checksum mismatch detected")
risk_score += (pe_risk / 100) * WEIGHTS['pe_info'] * 100
# 2. Static Analysis Risk Calculation
if static_results:
static_risk = 0
# YARA detections (static)
yara_matches = static_results.get('yara', {}).get('matches', [])
unique_yara_rules = len({match.get('rule') for match in yara_matches if match.get('rule')})
if unique_yara_rules > 0:
static_risk += min(unique_yara_rules * 15, 50) # Cap at 50 points
risk_factors.append(f"Found {unique_yara_rules} YARA rule matches (static)")
# CheckPLZ findings
checkplz_findings = static_results.get('checkplz', {}).get('findings', {})
if checkplz_findings.get('initial_threat'):
static_risk += 50
risk_factors.append("CheckPLZ detected initial threat indicators")
risk_score += (static_risk / 100) * WEIGHTS['static'] * 100
# 3. Dynamic Analysis Risk Calculation
if dynamic_results:
dynamic_risk = 0
# YARA detections (dynamic)
yara_matches = dynamic_results.get('yara', {}).get('matches', [])
unique_yara_rules = len({match.get('rule') for match in yara_matches if match.get('rule')})
if unique_yara_rules > 0:
dynamic_risk += min(unique_yara_rules * 15, 40) # Cap at 40 points
risk_factors.append(f"Found {unique_yara_rules} YARA rule matches (dynamic)")
# PE-Sieve detections
pesieve_suspicious = int(dynamic_results.get('pe_sieve', {})
.get('findings', {}).get('total_suspicious', 0))
if pesieve_suspicious > 0:
dynamic_risk += min(pesieve_suspicious * 20, 40) # Cap at 40 points
risk_factors.append(f"PE-Sieve found {pesieve_suspicious} suspicious indicators")
# Moneta memory anomalies
moneta_findings = dynamic_results.get('moneta', {}).get('findings', {})
memory_anomalies = sum([
int(moneta_findings.get('total_private_rwx', 0) or 0),
int(moneta_findings.get('total_private_rx', 0) or 0),
int(moneta_findings.get('total_modified_code', 0) or 0),
int(moneta_findings.get('total_heap_executable', 0) or 0),
int(moneta_findings.get('total_modified_pe_header', 0) or 0),
int(moneta_findings.get('total_inconsistent_x', 0) or 0),
int(moneta_findings.get('total_missing_peb', 0) or 0),
int(moneta_findings.get('total_mismatching_peb', 0) or 0)
])
if memory_anomalies > 0:
dynamic_risk += min(memory_anomalies * 10, 30) # Cap at 30 points
risk_factors.append(f"Found {memory_anomalies} memory anomalies")
# Patriot detections
patriot_findings = len(dynamic_results.get('patriot', {})
.get('findings', {}).get('findings', []))
if patriot_findings > 0:
dynamic_risk += min(patriot_findings * 15, 30) # Cap at 30 points
risk_factors.append(f"Found {patriot_findings} suspicious behaviors")
# HSB detections
hsb_findings = dynamic_results.get('hsb', {}).get('findings', {})
if hsb_findings and hsb_findings.get('detections'):
hsb_detections = len(hsb_findings['detections'][0].get('findings', []))
if hsb_detections > 0:
dynamic_risk += min(hsb_detections * 20, 40) # Cap at 40 points
risk_factors.append(f"Found {hsb_detections} HSB detections")
risk_score += (dynamic_risk / 100) * WEIGHTS['dynamic'] * 100
# Normalize final score to 0-100 range and round to 2 decimal places
risk_score = round(min(max(risk_score, 0), 100), 2)
return risk_score, risk_factors
def get_risk_level(risk_score):
"""
Convert numerical risk score to categorical risk level
"""
if risk_score >= 75:
return "Critical"
elif risk_score >= 50:
return "High"
elif risk_score >= 25:
return "Medium"
else:
return "Low"
def register_routes(app):
analysis_manager = AnalysisManager(app.config)
@@ -423,9 +573,62 @@ def register_routes(app):
with open(file_info_path, 'r') as f:
file_info = json.load(f)
# Load static and dynamic results if they exist
static_results = None
dynamic_results = None
static_path = os.path.join(result_path, 'static_analysis_results.json')
if os.path.exists(static_path):
with open(static_path, 'r') as f:
static_results = json.load(f)
dynamic_path = os.path.join(result_path, 'dynamic_analysis_results.json')
if os.path.exists(dynamic_path):
with open(dynamic_path, 'r') as f:
dynamic_results = json.load(f)
# Calculate overall risk
risk_score, risk_factors = calculate_file_risk(file_info, static_results, dynamic_results)
risk_level = get_risk_level(risk_score)
# Add risk information to file_info
file_info['risk_assessment'] = {
'score': risk_score,
'level': risk_level,
'factors': risk_factors
}
if analysis_type == 'info':
return render_template('file_info.html', file_info=file_info)
# Add helper data for the template
if 'pe_info' in file_info:
# Calculate section entropy risk levels
for section in file_info['pe_info']['sections']:
section['entropy_risk'] = get_entropy_risk_level(section['entropy'])
# Group suspicious imports by DLL
grouped_imports = {}
for imp in file_info['pe_info'].get('suspicious_imports', []):
dll = imp['dll']
if dll not in grouped_imports:
grouped_imports[dll] = []
grouped_imports[dll].append(imp)
file_info['pe_info']['grouped_suspicious_imports'] = grouped_imports
# Format checksum values
if 'checksum_info' in file_info['pe_info']:
checksum = file_info['pe_info']['checksum_info']
checksum['stored_checksum'] = format_hex(checksum['stored_checksum'])
checksum['calculated_checksum'] = format_hex(checksum['calculated_checksum'])
return render_template('file_info.html',
file_info=file_info,
entropy_risk_levels={
'High': 7.2,
'Medium': 6.8,
'Low': 0
})
elif analysis_type in ['static', 'dynamic']:
results_file = f'{analysis_type}_analysis_results.json'
@@ -437,30 +640,29 @@ def register_routes(app):
analysis_results = json.load(f)
if analysis_type == 'static':
# Calculate detection counts for static analysis with safe defaults
# Calculate detection counts for static analysis with safe defaults and proper error handling
try:
yara_matches = analysis_results.get('yara', {}).get('matches', [])
yara_detections = len(yara_matches) if yara_matches is not None else 0
except:
yara_detections = len({match.get('rule') for match in yara_matches}) if isinstance(yara_matches, list) else 0
except (TypeError, ValueError):
yara_detections = 0
try:
checkplz_findings = analysis_results.get('checkplz', {}).get('findings', {})
checkplz_detections = 1 if checkplz_findings and checkplz_findings.get('initial_threat') else 0
except:
checkplz_detections = 0
checkplz_detections = 0
checkplz_findings = analysis_results.get('checkplz', {}).get('findings', {})
if isinstance(checkplz_findings, dict):
checkplz_detections = 1 if checkplz_findings.get('initial_threat') else 0
# Format scan duration as MM:SS.mmm
# Format scan duration with proper error handling
formatted_duration = "00:00.000"
try:
scan_duration = analysis_results.get('checkplz', {}).get('findings', {}).get('scan_results', {}).get('scan_duration', 0)
if scan_duration is None:
scan_duration = 0
scan_duration = float(analysis_results.get('checkplz', {}).get('findings', {})
.get('scan_results', {}).get('scan_duration', 0))
minutes = int(scan_duration // 60)
seconds = int(scan_duration % 60)
milliseconds = int((scan_duration % 1) * 1000)
formatted_duration = f"{minutes:02d}:{seconds:02d}.{milliseconds:03d}"
except:
formatted_duration = "00:00.000"
except (TypeError, ValueError, AttributeError):
pass
return render_template('static_analysis.html',
file_info=file_info,
@@ -470,26 +672,62 @@ def register_routes(app):
scan_duration=formatted_duration)
elif analysis_type == 'dynamic':
# Calculate detection counts for dynamic analysis
yara_detections = len(analysis_results.get('yara', {}).get('matches', [])) if analysis_results.get('yara') else 0
pesieve_detections = analysis_results.get('pe_sieve', {}).get('findings', {}).get('total_suspicious', 0)
moneta_detections = (
analysis_results.get('moneta', {}).get('findings', {}).get('total_private_rwx', 0) +
analysis_results.get('moneta', {}).get('findings', {}).get('total_abnormal_private_exec', 0)
)
patriot_detections = len(analysis_results.get('patriot', {}).get('findings', {}).get('findings', []))
hsb_detections = analysis_results.get('hsb', {}).get('findings', {}).get('summary', {}).get('total_findings', 0)
# YARA: Count unique rule matches
try:
yara_matches = analysis_results.get('yara', {}).get('matches', [])
yara_detections = len({match.get('rule') for match in yara_matches}) if yara_matches else 0
except (TypeError, ValueError):
yara_detections = 0
# PE-Sieve: Get total suspicious count
try:
pesieve_findings = analysis_results.get('pe_sieve', {}).get('findings', {})
pesieve_detections = int(pesieve_findings.get('total_suspicious', 0) or 0)
except (TypeError, ValueError):
pesieve_detections = 0
# Moneta: Count memory anomalies including PEB violations
try:
moneta_findings = analysis_results.get('moneta', {}).get('findings', {})
moneta_detections = sum([
int(moneta_findings.get('total_private_rwx', 0) or 0),
int(moneta_findings.get('total_private_rx', 0) or 0),
int(moneta_findings.get('total_modified_code', 0) or 0),
int(moneta_findings.get('total_heap_executable', 0) or 0),
int(moneta_findings.get('total_modified_pe_header', 0) or 0),
int(moneta_findings.get('total_inconsistent_x', 0) or 0),
int(moneta_findings.get('total_missing_peb', 0) or 0),
int(moneta_findings.get('total_mismatching_peb', 0) or 0)
])
except (TypeError, ValueError):
moneta_detections = 0
# Patriot: Get findings directly from findings array length
try:
patriot_findings = analysis_results.get('patriot', {}).get('findings', {}).get('findings', [])
patriot_detections = len(patriot_findings) if isinstance(patriot_findings, list) else 0
except (TypeError, ValueError):
patriot_detections = 0
# HSB: Count findings from severity counts and add any findings in the detections array
# HSB: Get total findings directly from detections[0]
try:
hsb_findings = analysis_results.get('hsb', {}).get('findings', {})
if hsb_findings and hsb_findings.get('detections'):
hsb_detections = len(hsb_findings['detections'][0].get('findings', []))
else:
hsb_detections = 0
except (TypeError, ValueError, IndexError):
hsb_detections = 0
return render_template('dynamic_analysis.html',
file_info=file_info,
analysis_results=analysis_results,
yara_detections=yara_detections,
pesieve_detections=pesieve_detections,
moneta_detections=moneta_detections,
patriot_detections=patriot_detections,
hsb_detections=hsb_detections)
return render_template('error.html', error='Invalid analysis type'), 400
file_info=file_info,
analysis_results=analysis_results,
yara_detections=yara_detections,
pesieve_detections=pesieve_detections,
moneta_detections=moneta_detections,
patriot_detections=patriot_detections,
hsb_detections=hsb_detections)
except Exception as e:
return render_template('error.html', error=str(e)), 500
@@ -646,8 +884,7 @@ def register_routes(app):
'issues': [str(e)]
}), 500
@app.route('/summary')
@app.route('/summary.html')
@app.route('/summary', methods=['GET'])
def summary_page():
"""Route for the summary page"""
return render_template('summary.html')
@@ -657,24 +894,37 @@ def register_routes(app):
try:
results_dir = app.config['upload']['result_folder']
summary = {}
# Iterate through all subdirectories in the results folder
for md5_dir in os.listdir(results_dir):
dir_path = os.path.join(results_dir, md5_dir)
if not os.path.isdir(dir_path):
continue
# Read file_info.json
file_info_path = os.path.join(dir_path, 'file_info.json')
if not os.path.exists(file_info_path):
continue
with open(file_info_path, 'r') as f:
file_info = json.load(f)
# Check for existence of analysis results
static_exists = os.path.exists(os.path.join(dir_path, 'static_analysis_results.json'))
dynamic_exists = os.path.exists(os.path.join(dir_path, 'dynamic_analysis_results.json'))
# Load static analysis results if they exist
static_results = None
static_path = os.path.join(dir_path, 'static_analysis_results.json')
if os.path.exists(static_path):
with open(static_path, 'r') as f:
static_results = json.load(f)
# Load dynamic analysis results if they exist
dynamic_results = None
dynamic_path = os.path.join(dir_path, 'dynamic_analysis_results.json')
if os.path.exists(dynamic_path):
with open(dynamic_path, 'r') as f:
dynamic_results = json.load(f)
# Calculate risk score using our comprehensive function
risk_score, risk_factors = calculate_file_risk(file_info, static_results, dynamic_results)
risk_level = get_risk_level(risk_score)
# Create summary for this file
summary[md5_dir] = {
@@ -686,8 +936,14 @@ def register_routes(app):
'result_dir_full_path': os.path.abspath(dir_path),
'entropy_value': file_info.get('entropy_analysis', {}).get('value', 0),
'detection_risk': file_info.get('entropy_analysis', {}).get('detection_risk', 'Unknown'),
'has_static_analysis': static_exists,
'has_dynamic_analysis': dynamic_exists
'has_static_analysis': os.path.exists(static_path),
'has_dynamic_analysis': os.path.exists(dynamic_path),
# Add the new risk assessment
'risk_assessment': {
'score': risk_score,
'level': risk_level,
'factors': risk_factors
}
}
return jsonify({
@@ -695,7 +951,6 @@ def register_routes(app):
'count': len(summary),
'files': summary
})
except Exception as e:
return jsonify({
'status': 'error',
+2
View File
@@ -1,3 +1,5 @@
// app/static/js/status.js
// Constants
const CONFIG = {
notificationDuration: 5000,
+47 -42
View File
@@ -43,39 +43,41 @@ function updateStats() {
const totalBytes = files.reduce((sum, file) => sum + (file.file_size || 0), 0);
elements.storageUsed.textContent = formatFileSize(totalBytes);
// Calculate average entropy and determine risk
const filesWithEntropy = files.filter(f => f.entropy_value);
// Calculate average risk score
const filesWithRisk = files.filter(f => f.risk_assessment && f.risk_assessment.score !== undefined);
if (filesWithEntropy.length > 0) {
const avgEntropy = filesWithEntropy.reduce((sum, file) => sum + file.entropy_value, 0) / filesWithEntropy.length;
if (filesWithRisk.length > 0) {
const avgRiskScore = filesWithRisk.reduce((sum, file) =>
sum + file.risk_assessment.score, 0) / filesWithRisk.length;
// Determine risk level based on entropy value
let riskText;
let riskClass;
// Determine risk level based on risk score
let riskText, riskClass;
if (avgEntropy >= 7.2) {
if (avgRiskScore >= 75) {
riskText = 'Critical';
riskClass = 'bg-red-900 text-white';
} else if (avgRiskScore >= 50) {
riskText = 'High';
riskClass = 'bg-red-500 text-white';
} else if (avgEntropy >= 6.8) {
} else if (avgRiskScore >= 25) {
riskText = 'Medium';
riskClass = 'bg-yellow-500 text-black';
} else {
riskText = 'Low';
riskClass = 'bg-green-500 text-white';
}
// console.log(avgEntropy);
elements.averageRisk.textContent = riskText;
elements.averageRisk.textContent = `${riskText} Risk`;
elements.averageRisk.className = 'px-2 py-1 text-sm rounded-lg inline-flex items-center justify-center font-medium ' + riskClass;
elements.averageEntropy.textContent = `Entropy: ${avgEntropy.toFixed(3)}`;
elements.averageEntropy.textContent = `Risk Score: ${avgRiskScore.toFixed(1)}%`;
} else {
elements.averageRisk.textContent = '-';
elements.averageRisk.className = 'px-2 py-1 text-sm rounded-lg inline-flex items-center justify-center font-medium bg-gray-500 text-white';
elements.averageEntropy.textContent = 'Entropy: -';
elements.averageEntropy.textContent = 'Risk Score: -';
}
}
// Render file list
// Render file list - Update the risk display part
function renderFiles() {
const filteredFiles = filterFiles(files);
const sortedFiles = sortFiles(filteredFiles);
@@ -90,34 +92,33 @@ function renderFiles() {
row.querySelector('[data-field="fileName"]').textContent = file.filename;
row.querySelector('[data-field="fileHash"]').textContent = file.md5;
// Entropy and Risk
const entropyEl = row.querySelector('[data-field="fileEntropy"]');
// Risk Assessment
const riskEl = row.querySelector('[data-field="fileRisk"]');
const entropyEl = row.querySelector('[data-field="fileEntropy"]');
if (file.entropy_value) {
entropyEl.textContent = `Entropy: ${file.entropy_value.toFixed(2)}`;
}
if (file.detection_risk) {
riskEl.textContent = file.detection_risk;
if (file.risk_assessment) {
const { level, score, factors } = file.risk_assessment;
riskEl.textContent = `${level} (${score}%)`;
riskEl.className = 'px-3 py-1 text-xs rounded-lg inline-flex items-center justify-center font-medium';
switch(file.detection_risk.toLowerCase()) {
case 'high':
// riskEl.className += ' bg-red-500/10 text-red-400 border border-red-900/20';
riskEl.className += ' bg-red-500 text-white';
break;
case 'medium':
// riskEl.className += ' bg-yellow-500/10 text-yellow-400 border border-yellow-900/20';
riskEl.className += ' bg-yellow-500 text-black';
break;
case 'low':
// riskEl.className += ' bg-green-500/10 text-green-400 border border-green-900/20';
riskEl.className += ' bg-green-500 text-white';
break;
default:
// riskEl.className += ' bg-gray-500/10 text-gray-400 border border-gray-900/20';
riskEl.className += ' bg-gray-500 text-white';
if (score >= 75) {
riskEl.className += ' bg-red-900 text-white';
} else if (score >= 50) {
riskEl.className += ' bg-red-500 text-white';
} else if (score >= 25) {
riskEl.className += ' bg-yellow-500 text-black';
} else {
riskEl.className += ' bg-green-500 text-white';
}
// Show first risk factor if available
if (factors && factors.length > 0) {
entropyEl.textContent = factors[0];
}
} else {
riskEl.textContent = 'Unknown';
riskEl.className += ' bg-gray-500 text-white px-3 py-1 text-xs rounded-lg inline-flex items-center justify-center font-medium';
entropyEl.textContent = '';
}
// File type
// const typeCell = row.querySelector('#fileType');
@@ -148,17 +149,18 @@ function renderFiles() {
}
// Filter files based on search and type
// Update filter function to use new risk levels
function filterFiles(files) {
const searchTerm = elements.searchFiles.value.toLowerCase();
const fileType = elements.filterType.value;
const riskLevel = elements.filterRisk.value;
const riskLevel = elements.filterRisk.value.toLowerCase();
return files.filter(file => {
const matchesSearch = file.filename.toLowerCase().includes(searchTerm) ||
file.md5.toLowerCase().includes(searchTerm);
const matchesType = fileType === 'all' || file.filename.toLowerCase().endsWith(fileType);
const matchesRisk = riskLevel === 'all' ||
(file.detection_risk && file.detection_risk.toLowerCase() === riskLevel);
(file.risk_assessment && file.risk_assessment.level.toLowerCase() === riskLevel);
return matchesSearch && matchesType && matchesRisk;
});
}
@@ -177,8 +179,8 @@ function sortFiles(files) {
return new Date(a.upload_time).getTime() - new Date(b.upload_time).getTime();
case 'size':
return (b.file_size || 0) - (a.file_size || 0);
case 'entropy':
return (b.entropy_value || 0) - (a.entropy_value || 0);
case 'risk':
return ((b.risk_assessment?.score || 0) - (a.risk_assessment?.score || 0));
default:
return 0;
}
@@ -204,6 +206,9 @@ function getAnalysisStatus(file) {
};
}
// app/static/js/summery.js
// View file details
function viewFile(md5) {
window.location.href = `/file/${md5}/info`;
+76 -13
View File
@@ -48,7 +48,6 @@
</div>
</div>
</div>
<!-- Scanner Results Table -->
<div class="bg-gray-900/30 rounded-lg border border-gray-800 overflow-hidden">
<table class="w-full">
@@ -70,8 +69,21 @@
</span>
</td>
<td class="px-6 py-4 text-base {{ 'text-red-500' if yara_detections else 'text-gray-400' }}">{{ yara_detections }}</td>
<td class="px-6 py-4 text-base text-gray-400">
{{ yara_detections|string + ' rule matches found' if yara_detections else 'No threats detected' }}
<td class="px-6 py-4">
{% if yara_detections %}
<div class="text-base text-gray-400">
{% for match in analysis_results.yara.matches %}
<div class="mb-1">
Rule: <span class="text-red-400">{{ match.rule }}</span>
{% if match.metadata %}
(Severity: {{ match.metadata.severity }})
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
<span class="text-base text-gray-400">No threats detected</span>
{% endif %}
</td>
</tr>
@@ -84,8 +96,17 @@
</span>
</td>
<td class="px-6 py-4 text-base {{ 'text-red-500' if pesieve_detections else 'text-gray-400' }}">{{ pesieve_detections }}</td>
<td class="px-6 py-4 text-base text-gray-400">
{{ pesieve_detections|string + ' suspicious modifications found' if pesieve_detections else 'No modifications detected' }}
<td class="px-6 py-4">
{% if pesieve_detections %}
{% set findings = analysis_results.pe_sieve.findings %}
<div class="text-base text-gray-400">
<div>Implanted: {{ findings.implanted }}</div>
<div>Implanted PE: {{ findings.implanted_pe }}</div>
<div>Implanted shellcode: {{ findings.implanted_shc }}</div>
</div>
{% else %}
<span class="text-base text-gray-400">No modifications detected</span>
{% endif %}
</td>
</tr>
@@ -98,8 +119,32 @@
</span>
</td>
<td class="px-6 py-4 text-base {{ 'text-red-500' if moneta_detections else 'text-gray-400' }}">{{ moneta_detections }}</td>
<td class="px-6 py-4 text-base text-gray-400">
{{ 'Memory anomalies found' if moneta_detections else 'No anomalies detected' }}
<td class="px-6 py-4">
{% if moneta_detections %}
{% set findings = analysis_results.moneta.findings %}
<div class="text-base text-gray-400">
{% if findings.total_private_rwx > 0 %}
<div>Private RWX: {{ findings.total_private_rwx }}</div>
{% endif %}
{% if findings.total_private_rx > 0 %}
<div>Private RX: {{ findings.total_private_rx }}</div>
{% endif %}
{% if findings.total_modified_code > 0 %}
<div>Modified Code: {{ findings.total_modified_code }}</div>
{% endif %}
{% if findings.total_heap_executable > 0 %}
<div>Heap Executable: {{ findings.total_heap_executable }}</div>
{% endif %}
{% if findings.total_missing_peb > 0 %}
<div>Missing PEB: {{ findings.total_missing_peb }}</div>
{% endif %}
{% if findings.total_mismatching_peb > 0 %}
<div>Mismatching PEB: {{ findings.total_mismatching_peb }}</div>
{% endif %}
</div>
{% else %}
<span class="text-base text-gray-400">No anomalies detected</span>
{% endif %}
</td>
</tr>
@@ -112,8 +157,18 @@
</span>
</td>
<td class="px-6 py-4 text-base {{ 'text-red-500' if patriot_detections else 'text-gray-400' }}">{{ patriot_detections }}</td>
<td class="px-6 py-4 text-base text-gray-400">
{{ patriot_detections|string + ' suspicious activities found' if patriot_detections else 'No suspicious activities' }}
<td class="px-6 py-4">
{% if patriot_detections %}
<div class="text-base text-gray-400">
{% for finding in analysis_results.patriot.findings.findings %}
<div class="mb-1">
{{ finding.type }} ({{ finding.level }})
</div>
{% endfor %}
</div>
{% else %}
<span class="text-base text-gray-400">No suspicious activities</span>
{% endif %}
</td>
</tr>
@@ -126,14 +181,22 @@
</span>
</td>
<td class="px-6 py-4 text-base {{ 'text-red-500' if hsb_detections else 'text-gray-400' }}">{{ hsb_detections }}</td>
<td class="px-6 py-4 text-base text-gray-400">
{{ 'Suspicious behavior detected' if hsb_detections else 'No suspicious behavior' }}
<td class="px-6 py-4">
{% if hsb_detections %}
<div class="text-base text-gray-400">
{% for detection in analysis_results.hsb.findings.detections %}
{% for finding in detection.findings %}
<div class="mb-1">{{ finding.type }} ({{ finding.severity }})</div>
{% endfor %}
{% endfor %}
</div>
{% else %}
<span class="text-base text-gray-400">No suspicious behavior</span>
{% endif %}
</td>
</tr>
</tbody>
</table>
</div>
<!-- Rest of your existing detailed results code here -->
</div>
{% endblock %}
+211 -90
View File
@@ -2,41 +2,121 @@
{% block content %}
<div class="max-w-6xl mx-auto px-4 py-6">
<!-- Header -->
<!-- Header with File Info -->
<div class="flex items-center justify-between mb-6">
<div>
<h1 class="text-2xl font-medium text-gray-100">{{ file_info.original_name }}</h1>
<p class="text-base text-gray-400 mt-1 font-mono">MD5: {{ file_info.md5 }}</p>
</div>
<span class="px-3 py-1 rounded-lg text-sm font-medium
{% if file_info.entropy_analysis.detection_risk == 'High' %}
bg-red-500 text-white
{% elif file_info.entropy_analysis.detection_risk == 'Medium' %}
bg-yellow-500 text-black
{% else %}
bg-green-500 text-white
{% endif %}">
{{ file_info.entropy_analysis.detection_risk }} Risk
</span>
<div class="flex items-center gap-3">
<!-- Entropy Badge -->
<div class="flex flex-col items-end">
<span class="text-sm text-gray-400">Entropy</span>
<span class="text-lg font-medium {% if file_info.entropy > entropy_risk_levels.High %}text-red-400{% elif file_info.entropy > entropy_risk_levels.Medium %}text-yellow-400{% else %}text-green-400{% endif %}">
{{ "%.2f"|format(file_info.entropy) }}
</span>
</div>
{# Replace the Risk Level Badge #}
<span class="px-3 py-1 rounded-lg text-sm font-medium
{% if file_info.risk_assessment.score >= 75 %}
bg-red-900/20 text-red-400 border border-red-900/20
{% elif file_info.risk_assessment.score >= 50 %}
bg-red-500/20 text-red-400 border border-red-900/20
{% elif file_info.risk_assessment.score >= 25 %}
bg-yellow-500/20 text-yellow-400 border border-yellow-900/20
{% else %}
bg-green-500/20 text-green-400 border border-green-900/20
{% endif %}">
{{ file_info.risk_assessment.level }} Risk ({{ file_info.risk_assessment.score }}%)
</span>
</div>
</div>
<!-- Add this after the header div -->
{# Add this right after the header div, before Navigation Buttons #}
<!-- Comprehensive Risk Assessment -->
<div class="bg-black/60 backdrop-blur-sm rounded-xl border border-gray-800 p-6 mb-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-lg font-medium text-gray-100">Risk Assessment</h2>
<span class="px-3 py-1 rounded-lg text-sm font-medium
{% if file_info.risk_assessment.level == 'Critical' %}
bg-red-900/20 text-red-400 border border-red-900/20
{% elif file_info.risk_assessment.level == 'High' %}
bg-red-500/20 text-red-400 border border-red-900/20
{% elif file_info.risk_assessment.level == 'Medium' %}
bg-yellow-500/20 text-yellow-400 border border-yellow-900/20
{% else %}
bg-green-500/20 text-green-400 border border-green-900/20
{% endif %}">
{{ file_info.risk_assessment.level }} Risk ({{ file_info.risk_assessment.score }}%)
</span>
</div>
<!-- Risk Score Visualization -->
<div class="w-full h-3 bg-gray-800 rounded-full mb-4 overflow-hidden">
<div class="h-full rounded-full transition-all duration-500
{% if file_info.risk_assessment.level == 'Critical' %}
bg-red-900
{% elif file_info.risk_assessment.level == 'High' %}
bg-red-500
{% elif file_info.risk_assessment.level == 'Medium' %}
bg-yellow-500
{% else %}
bg-green-500
{% endif %}"
style="width: {{ file_info.risk_assessment.score }}%">
</div>
</div>
<!-- Risk Factors -->
{% if file_info.risk_assessment.factors %}
<div class="space-y-2">
<h3 class="text-sm font-medium text-gray-400 mb-3">Risk Factors</h3>
{% for factor in file_info.risk_assessment.factors %}
<div class="flex items-start gap-2 bg-gray-800/50 p-3 rounded-lg">
<svg class="w-5 h-5 text-gray-400 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span class="text-gray-300">{{ factor }}</span>
</div>
{% endfor %}
</div>
{% endif %}
</div>
<!-- Navigation Buttons -->
<div class="flex items-center gap-4 mb-6">
<button onclick="window.location.href='/file/{{ file_info.md5 }}/static'"
class="px-4 py-2 bg-blue-500/10 text-blue-400 border border-blue-900/20 rounded-lg hover:bg-blue-500/20 transition-colors flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
<span>Static Analysis Results</span>
<span>Static Analysis</span>
</button>
<button onclick="window.location.href='/file/{{ file_info.md5 }}/dynamic'"
class="px-4 py-2 bg-green-500/10 text-green-400 border border-green-900/20 rounded-lg hover:bg-green-500/20 transition-colors flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
<span>Dynamic Analysis Results</span>
<span>Dynamic Analysis</span>
</button>
</div>
<!-- Basic Info Card -->
<!-- File Hash Information -->
<div class="bg-black/60 backdrop-blur-sm rounded-xl border border-gray-800 p-6 mb-6">
<h2 class="text-lg font-medium text-gray-100 mb-4">File Hashes</h2>
<div class="space-y-3">
<div>
<p class="text-sm text-gray-400">MD5</p>
<p class="text-gray-200 font-mono text-sm break-all">{{ file_info.md5 }}</p>
</div>
<div>
<p class="text-sm text-gray-400">SHA256</p>
<p class="text-gray-200 font-mono text-sm break-all">{{ file_info.sha256 }}</p>
</div>
</div>
</div>
<!-- Basic Information -->
<div class="bg-black/60 backdrop-blur-sm rounded-xl border border-gray-800 p-6 mb-6">
<h2 class="text-lg font-medium text-gray-100 mb-4">Basic Information</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
@@ -48,26 +128,31 @@
<p class="text-sm text-gray-400">File Type</p>
<p class="text-gray-200">{{ file_info.mime_type }}</p>
</div>
<div>
<p class="text-sm text-gray-400">Extension</p>
<p class="text-gray-200">{{ file_info.extension }}</p>
</div>
<div>
<p class="text-sm text-gray-400">Upload Time</p>
<p class="text-gray-200">{{ file_info.upload_time }}</p>
</div>
<div>
<p class="text-sm text-gray-400">SHA256</p>
<p class="text-gray-200 font-mono text-sm break-all">{{ file_info.sha256 }}</p>
</div>
<div>
<p class="text-sm text-gray-400">Entropy</p>
<p class="text-gray-200">{{ "%.2f"|format(file_info.entropy) }}</p>
</div>
</div>
</div>
{% if file_info.pe_info %}
<!-- PE Info -->
<!-- PE Information -->
<div class="bg-black/60 backdrop-blur-sm rounded-xl border border-gray-800 p-6 mb-6">
<h2 class="text-lg font-medium text-gray-100 mb-4">PE Information</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="flex items-center justify-between mb-4">
<h2 class="text-lg font-medium text-gray-100">PE Information</h2>
{% if file_info.pe_info.detection_notes %}
<span class="px-3 py-1 bg-red-500/20 text-red-400 rounded-lg text-sm">
{{ file_info.pe_info.detection_notes|length }} Detection Notes
</span>
{% endif %}
</div>
<!-- PE Basic Info -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
<div>
<p class="text-sm text-gray-400">File Type</p>
<p class="text-gray-200">{{ file_info.pe_info.file_type }}</p>
@@ -88,79 +173,115 @@
<p class="text-sm text-gray-400">Entry Point</p>
<p class="text-gray-200 font-mono">{{ file_info.pe_info.entry_point }}</p>
</div>
{% if file_info.pe_info.checksum_info %}
<div>
<p class="text-sm text-gray-400">PE Checksum</p>
<p class="text-gray-200 font-mono">
{% if file_info.pe_info.checksum_info.is_valid %}
<span class="text-green-400">Valid</span>
{% else %}
<span class="text-red-400">Invalid</span>
{% endif %}
</p>
</div>
{% endif %}
</div>
</div>
<!-- Detection Notes -->
<div class="bg-black/60 backdrop-blur-sm rounded-xl border border-gray-800 p-6 mb-6">
<h2 class="text-lg font-medium text-gray-100 mb-4">Detection Notes</h2>
<ul class="space-y-2">
{% if file_info.pe_info.detection_notes %}
<!-- Detection Notes -->
<div class="space-y-2 mb-6">
<h3 class="text-sm font-medium text-gray-400 mb-3">Detection Notes</h3>
{% for note in file_info.pe_info.detection_notes %}
<li class="flex items-start gap-2">
<svg class="w-5 h-5 text-red-500 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<div class="flex items-start gap-2 bg-red-500/10 p-3 rounded-lg border border-red-900/20">
<svg class="w-5 h-5 text-red-400 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>
<span class="text-gray-300">{{ note }}</span>
</li>
{% endfor %}
</ul>
</div>
<!-- PE Sections -->
<div class="bg-black/60 backdrop-blur-sm rounded-xl border border-gray-800 p-6 mb-6">
<h2 class="text-lg font-medium text-gray-100 mb-4">PE Sections</h2>
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-gray-800">
<th class="text-left py-3 text-gray-400">Name</th>
<th class="text-left py-3 text-gray-400">Size</th>
<th class="text-left py-3 text-gray-400">Entropy</th>
<th class="text-left py-3 text-gray-400">Notes</th>
</tr>
</thead>
<tbody>
{% for section in file_info.pe_info.sections %}
<tr class="border-b border-gray-800">
<td class="py-3">
<span class="font-mono {% if section.is_standard %}text-gray-300{% else %}text-yellow-500{% endif %}">
{{ section.name }}
</span>
</td>
<td class="py-3 text-gray-300">{{ section.size|filesizeformat }}</td>
<td class="py-3">
<span class="{% if section.entropy > 7.2 %}text-red-500{% else %}text-gray-300{% endif %}">
{{ "%.2f"|format(section.entropy) }}
</span>
</td>
<td class="py-3">
<div class="space-y-1">
{% for note in section.detection_notes %}
<div class="text-sm text-red-400">{{ note }}</div>
{% endfor %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- Suspicious Imports -->
{% if file_info.pe_info.suspicious_imports %}
<div class="bg-black/60 backdrop-blur-sm rounded-xl border border-gray-800 p-6">
<h2 class="text-lg font-medium text-gray-100 mb-4">Suspicious Imports</h2>
<div class="space-y-4">
{% for import in file_info.pe_info.suspicious_imports %}
<div class="p-4 bg-red-500/10 rounded-lg border border-red-900/20">
<p class="text-red-400 font-mono">{{ import.dll }}!{{ import.function }}</p>
<p class="text-sm text-gray-400 mt-1">{{ import.note }}</p>
</div>
{% endfor %}
</div>
{% endif %}
{% if file_info.pe_info.checksum_info and not file_info.pe_info.checksum_info.is_valid %}
<!-- Checksum Details -->
<div class="bg-yellow-500/10 p-4 rounded-lg border border-yellow-900/20 mb-6">
<h3 class="text-sm font-medium text-yellow-400 mb-2">Checksum Mismatch</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<p class="text-sm text-gray-400">Stored Checksum</p>
<p class="text-gray-200 font-mono">{{ file_info.pe_info.checksum_info.stored_checksum }}</p>
</div>
<div>
<p class="text-sm text-gray-400">Calculated Checksum</p>
<p class="text-gray-200 font-mono">{{ file_info.pe_info.checksum_info.calculated_checksum }}</p>
</div>
</div>
</div>
{% endif %}
<!-- PE Sections -->
<div class="mb-6">
<h3 class="text-sm font-medium text-gray-400 mb-3">PE Sections</h3>
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-gray-800">
<th class="text-left py-3 text-gray-400">Name</th>
<th class="text-left py-3 text-gray-400">Size</th>
<th class="text-left py-3 text-gray-400">Entropy</th>
<th class="text-left py-3 text-gray-400">Notes</th>
</tr>
</thead>
<tbody>
{% for section in file_info.pe_info.sections %}
<tr class="border-b border-gray-800">
<td class="py-3">
<span class="font-mono {% if section.is_standard %}text-gray-300{% else %}text-yellow-500{% endif %}">
{{ section.name }}
</span>
</td>
<td class="py-3 text-gray-300">{{ section.size|filesizeformat }}</td>
<td class="py-3">
<span class="{% if section.entropy > 7.2 %}text-red-500{% elif section.entropy > 6.8 %}text-yellow-500{% else %}text-gray-300{% endif %}">
{{ "%.2f"|format(section.entropy) }}
</span>
</td>
<td class="py-3">
<div class="space-y-1">
{% for note in section.detection_notes %}
<div class="text-sm text-red-400">{{ note }}</div>
{% endfor %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- Suspicious Imports -->
{% if file_info.pe_info.suspicious_imports %}
<div>
<h3 class="text-sm font-medium text-gray-400 mb-3">Suspicious Imports</h3>
<div class="space-y-4">
{% for dll, imports in file_info.pe_info.grouped_suspicious_imports.items() %}
<div class="bg-red-500/10 p-4 rounded-lg border border-red-900/20">
<h4 class="text-red-400 font-mono mb-2">{{ dll }}</h4>
<div class="space-y-2">
{% for import in imports %}
<div class="pl-4 border-l-2 border-red-900/20">
<p class="text-gray-300 font-mono">{{ import.function }}</p>
<p class="text-sm text-gray-400 mt-1">{{ import.note }}</p>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endif %}
{% endif %}
</div>
{% endblock %}
+15 -2
View File
@@ -66,8 +66,21 @@
</span>
</td>
<td class="px-6 py-4 text-base {{ 'text-red-500' if yara_detections else 'text-gray-400' }}">{{ yara_detections }}</td>
<td class="px-6 py-4 text-base text-gray-400">
{{ yara_detections|string + ' rule matches found' if yara_detections else 'No threats detected' }}
<td class="px-6 py-4">
{% if yara_detections %}
<div class="text-base text-gray-400">
{% for match in analysis_results.yara.matches %}
<div class="mb-1">
Rule: <span class="text-red-400">{{ match.rule }}</span>
{% if match.metadata %}
(Severity: {{ match.metadata.severity }})
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
<span class="text-base text-gray-400">No threats detected</span>
{% endif %}
</td>
</tr>
<!-- CheckPlz Results Row -->
+7 -2
View File
@@ -1,3 +1,5 @@
<!-- app/templates/summery.html -->
{% extends "base.html" %}
{% block page_title %}File Management{% endblock %}
@@ -83,16 +85,19 @@
</select>
<select id="filterRisk" class="bg-gray-900/30 border border-gray-800 rounded-lg px-3 py-2 text-gray-300 focus:outline-none focus:border-red-500 focus:ring-1 focus:ring-red-500/50">
<option value="all">All Risks</option>
<option value="critical">Critical Risk</option>
<option value="high">High Risk</option>
<option value="medium">Medium Risk</option>
<option value="low">Low Risk</option>
</select>
<select id="sortBy"
class="bg-gray-900/30 border border-gray-800 rounded-lg px-3 py-2 text-gray-300 focus:outline-none focus:border-red-500 focus:ring-1 focus:ring-red-500/50">
<!-- Add risk sorting option -->
<select id="sortBy" class="bg-gray-900/30 border border-gray-800 rounded-lg px-3 py-2 text-gray-300 focus:outline-none focus:border-red-500 focus:ring-1 focus:ring-red-500/50">
<option value="newest">Newest First</option>
<option value="oldest">Oldest First</option>
<option value="name">Name</option>
<option value="size">Size</option>
<option value="risk">Risk Score</option>
</select>
</div>
</div>