Summary page: include EDR runs in /files aggregation + per-row badge
This commit is contained in:
@@ -116,18 +116,51 @@ def process_file_summary(item, item_path, file_based_summary, logger):
|
||||
dynamic_results = json_helpers.load_json_file(dynamic_path)
|
||||
logger.debug(f"Loaded dynamic analysis results for item: {item}")
|
||||
|
||||
# Discover every EDR profile run for this sample. Stored as
|
||||
# `edr_<profile>_results.json`. The calculate_risk helper
|
||||
# accepts a {profile_name: findings} mapping.
|
||||
edr_results = {}
|
||||
edr_prefix, edr_suffix = 'edr_', '_results.json'
|
||||
for entry in os.listdir(item_path):
|
||||
if entry.startswith(edr_prefix) and entry.endswith(edr_suffix):
|
||||
profile_name = entry[len(edr_prefix):-len(edr_suffix)]
|
||||
loaded = json_helpers.load_json_file(os.path.join(item_path, entry))
|
||||
if loaded:
|
||||
edr_results[profile_name] = loaded
|
||||
|
||||
has_static_analysis = os.path.exists(static_path)
|
||||
has_dynamic_analysis = os.path.exists(dynamic_path)
|
||||
has_edr_analysis = bool(edr_results)
|
||||
|
||||
risk_score, risk_factors = risk_analyzer.calculate_risk(
|
||||
analysis_type='file',
|
||||
file_info=file_info,
|
||||
static_results=static_results,
|
||||
dynamic_results=dynamic_results,
|
||||
edr_results=edr_results or None,
|
||||
)
|
||||
|
||||
risk_level = risk_analyzer.get_risk_level(risk_score)
|
||||
|
||||
# Collapse the per-profile EDR runs into a small list the UI can
|
||||
# show in the status column without loading the full JSON. Each
|
||||
# entry has just the headline: profile, alert count, killed/AV
|
||||
# flags, status string.
|
||||
edr_runs = []
|
||||
if not is_driver:
|
||||
for profile_name, edr in (edr_results or {}).items():
|
||||
summary = (edr or {}).get('summary') or {}
|
||||
exec_block = (edr or {}).get('execution') or {}
|
||||
edr_runs.append({
|
||||
'profile': profile_name,
|
||||
'display_name': edr.get('display_name') or profile_name,
|
||||
'status': edr.get('status'),
|
||||
'total_alerts': summary.get('total_alerts') or len(edr.get('alerts') or []),
|
||||
'high_severity_alerts': summary.get('high_severity_alerts'),
|
||||
'blocked_by_av': summary.get('blocked_by_av'),
|
||||
'killed_by_edr': exec_block.get('killed_by_edr'),
|
||||
})
|
||||
|
||||
file_based_summary[item] = {
|
||||
'md5': file_info.get('md5', 'unknown'),
|
||||
'sha256': file_info.get('sha256', 'unknown'),
|
||||
@@ -139,6 +172,8 @@ def process_file_summary(item, item_path, file_based_summary, logger):
|
||||
'detection_risk': file_info.get('entropy_analysis', {}).get('detection_risk', 'Unknown'),
|
||||
'has_static_analysis': has_static_analysis,
|
||||
'has_dynamic_analysis': has_dynamic_analysis,
|
||||
'has_edr_analysis': has_edr_analysis if not is_driver else False,
|
||||
'edr_runs': edr_runs,
|
||||
'risk_assessment': {
|
||||
'score': risk_score,
|
||||
'level': risk_level,
|
||||
|
||||
@@ -160,8 +160,8 @@ function renderFiles() {
|
||||
|
||||
const statusCell = row.querySelector('[data-field="fileAnalysisStatus"]');
|
||||
const status = getAnalysisStatus(file);
|
||||
statusCell.className = `px-2 py-1 text-sm rounded-lg ${status.class}`;
|
||||
statusCell.textContent = status.text;
|
||||
statusCell.className = '';
|
||||
statusCell.innerHTML = renderFileStatusCell(file, status);
|
||||
|
||||
const viewButton = row.querySelector('[data-action="view"]');
|
||||
const deleteButton = row.querySelector('[data-action="delete"]');
|
||||
@@ -428,6 +428,38 @@ function getAnalysisStatus(file) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Status cell for one file row. Two parts:
|
||||
* - the static-vs-dynamic completion badge (Complete / Partial / No Results)
|
||||
* - an EDR sub-line listing each profile run with alert counts and
|
||||
* block/kill flags. Only rendered when the file has been dispatched
|
||||
* to at least one EDR profile.
|
||||
*/
|
||||
function renderFileStatusCell(file, status) {
|
||||
const main = `<span class="px-2 py-1 text-sm rounded-lg ${status.class}">${status.text}</span>`;
|
||||
if (!file.has_edr_analysis || !Array.isArray(file.edr_runs) || !file.edr_runs.length) {
|
||||
return main;
|
||||
}
|
||||
const badges = file.edr_runs.map(r => {
|
||||
const alerts = r.total_alerts || 0;
|
||||
const high = r.high_severity_alerts || 0;
|
||||
const blocked = !!r.blocked_by_av;
|
||||
const killed = !!r.killed_by_edr;
|
||||
const tone =
|
||||
blocked || killed || high > 0 ? 'critical' :
|
||||
alerts > 0 ? 'medium' :
|
||||
'info';
|
||||
const detail = (
|
||||
blocked ? `${r.display_name}: blocked` :
|
||||
killed ? `${r.display_name}: killed (${alerts})` :
|
||||
alerts > 0 ? `${r.display_name}: ${alerts}` :
|
||||
`${r.display_name}: clean`
|
||||
);
|
||||
return `<span class="lb-tag ${tone}" style="font-size: 10px; margin-right: 4px;">${detail}</span>`;
|
||||
}).join('');
|
||||
return `${main}<div style="margin-top: 4px; display: flex; flex-wrap: wrap; gap: 2px;">${badges}</div>`;
|
||||
}
|
||||
|
||||
function getDriverAnalysisStatus(driver) {
|
||||
if (driver.has_static_analysis) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user