Summary page: include EDR runs in /files aggregation + per-row badge

This commit is contained in:
BlackSnufkin
2026-04-30 01:49:37 -07:00
parent 4857d157f1
commit 532f2bfe45
2 changed files with 69 additions and 2 deletions
+35
View File
@@ -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,
+34 -2
View File
@@ -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 {