Improved Summery Section
This commit is contained in:
+299
-44
@@ -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',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// app/static/js/status.js
|
||||
|
||||
// Constants
|
||||
const CONFIG = {
|
||||
notificationDuration: 5000,
|
||||
|
||||
+47
-42
@@ -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`;
|
||||
|
||||
@@ -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
@@ -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 %}
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user