diff --git a/CHANGELOG.md b/CHANGELOG.md index b1cfb4a..6c57bd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0. ## [Unreleased] +## [3.1.2] — 2026-09-29 + +### Changed +- Explorer **Findings are newest first.** Severity tiers stay (Act now, + Serious, Worth a look, Minor), but inside each tier the kind of finding + seen most recently comes first, and inside a kind the latest occurrence + is shown first. Findings with no timestamp (configuration, file content) + follow the dated ones. + ## [3.1.1] — 2026-09-29 Precision pass on a real machine's case (2,495 findings): CRITICAL 19 → 14, diff --git a/internal/serve/shortcuts_test.go b/internal/serve/shortcuts_test.go index c72abc8..a45e82c 100644 --- a/internal/serve/shortcuts_test.go +++ b/internal/serve/shortcuts_test.go @@ -15,6 +15,9 @@ func TestShortcutsMatchPhysicalKeys(t *testing.T) { t.Errorf("ui.html lost %q", want) } } + if !strings.Contains(ui, "b.latest.localeCompare(a.latest)") { + t.Error("findings groups are no longer ordered newest first within a tier") + } if strings.Contains(ui, "e.key.toLowerCase()==='k'") { t.Error("layout-dependent Cmd+K check is back") } diff --git a/internal/serve/ui.html b/internal/serve/ui.html index 7614b73..6f02a5a 100644 --- a/internal/serve/ui.html +++ b/internal/serve/ui.html @@ -724,8 +724,12 @@ if(f.session) g.sessions.add(f.session); if(f.class!=='building_block') g.routine=false; }); const CR={HIGH:3,MEDIUM:2,LOW:1}; - S.groups=Object.values(m).map(g=>{ g.items.sort((a,b)=>sevOf(b.severity).r-sevOf(a.severity).r||(CR[b.confidence]||0)-(CR[a.confidence]||0)||String(b.timestamp).localeCompare(String(a.timestamp))); g.p=plain(g.items[0]); if(g.routine) g.p.kind='routine'; return g; }) - .sort((a,b)=>sevOf(b.sev).r-sevOf(a.sev).r||(CR[b.conf]||0)-(CR[a.conf]||0)||b.items.length-a.items.length); + // Newest first: within a severity tier, the kind seen most recently comes + // first, and inside a kind the latest occurrence is shown first. Undated + // findings (configuration, file content) sort after dated ones. + const ts=x=>String(x.timestamp||''); + S.groups=Object.values(m).map(g=>{ g.items.sort((a,b)=>ts(b).localeCompare(ts(a))||sevOf(b.severity).r-sevOf(a.severity).r||(CR[b.confidence]||0)-(CR[a.confidence]||0)); g.latest=g.items.reduce((t,x)=>ts(x)>t?ts(x):t,''); g.p=plain(g.items[0]); if(g.routine) g.p.kind='routine'; return g; }) + .sort((a,b)=>sevOf(b.sev).r-sevOf(a.sev).r||b.latest.localeCompare(a.latest)||(CR[b.conf]||0)-(CR[a.conf]||0)||b.items.length-a.items.length); } const tierOf=g=>g.p.kind==='routine'&&sevOf(g.sev).r<=2?'background':(g.sev==='CRITICAL'||g.sev==='HIGH')?'attention':g.sev==='MEDIUM'?'look':'background'; const reviewed=g=>g.items.filter(f=>verdictOf(f.key)).length; diff --git a/internal/version/version.go b/internal/version/version.go index 8163f59..2bf8a13 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -3,7 +3,7 @@ package version // Version is the collector version. Overridable at build time via // -ldflags "-X github.com/efij/AgentDFIR/v3/internal/version.Version=vX.Y.Z". -var Version = "3.1.1" +var Version = "3.1.2" // ADFIRVersion is the evidence package format version this binary writes. //