Skip to content

Commit 62fe1d9

Browse files
authored
Merge pull request #6 from p80n-sec/sort-findings-newest-first
Sort the findings tables newest disclosure first
2 parents b1519dc + 2fb0691 commit 62fe1d9

7 files changed

Lines changed: 78 additions & 9 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ Browse the full inventory, including per-finding detail pages and talks:
1010
<!-- BEGIN:published -->
1111
| ID | Date | Project | CVSS | Links |
1212
|---|---|---|---|---|
13+
| [CVE-2026-27959](https://nvd.nist.gov/vuln/detail/CVE-2026-27959) | 2026-02-26 | Koa | 7.5 | [Writeup](https://www.endorlabs.com/learn/cve-2026-27959-koa) |
1314
| [CVE-2025-63662](https://nvd.nist.gov/vuln/detail/CVE-2025-63662) | 2025-12-22 | GT Edge AI Platform | 7.5 | [Writeup](https://gist.github.com/p80n-sec/48ce34c929e8b946f0ad25f76e7b8cef) · [BSides NoVA 2025](https://www.p80n.com/#attacking-ai-applications) · [CackalackyCon 2026](https://www.p80n.com/#from-ctfs-to-cves) |
1415
| [CVE-2025-63663](https://nvd.nist.gov/vuln/detail/CVE-2025-63663) | 2025-12-22 | GT Edge AI Platform | 7.5 | [Writeup](https://gist.github.com/p80n-sec/f3ca933480157cb4e18c387d92f4d0c2) · [BSides NoVA 2025](https://www.p80n.com/#attacking-ai-applications) · [CackalackyCon 2026](https://www.p80n.com/#from-ctfs-to-cves) |
1516
| [CVE-2025-63664](https://nvd.nist.gov/vuln/detail/CVE-2025-63664) | 2025-12-22 | GT Edge AI Platform | 7.5 | [Writeup](https://gist.github.com/p80n-sec/0a0a71a2190d5e6f8083bf6069e7b5f2) · [BSides NoVA 2025](https://www.p80n.com/#attacking-ai-applications) · [CackalackyCon 2026](https://www.p80n.com/#from-ctfs-to-cves) |
1617
| [CVE-2025-63665](https://nvd.nist.gov/vuln/detail/CVE-2025-63665) | 2025-12-19 | GT Edge AI Platform | 9.8 | [Writeup](https://gist.github.com/p80n-sec/e5eefcef155e9dd14aaaaa49f9f94cd1) · [BSides NoVA 2025](https://www.p80n.com/#attacking-ai-applications) · [CackalackyCon 2026](https://www.p80n.com/#from-ctfs-to-cves) |
17-
| [CVE-2026-27959](https://nvd.nist.gov/vuln/detail/CVE-2026-27959) | 2026-02-26 | Koa | 7.5 | [Writeup](https://www.endorlabs.com/learn/cve-2026-27959-koa) |
1818
<!-- END:published -->
1919

2020
CVSS scores are CISA-ADP assessments where NVD analysis is still pending.

tools/research/records.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,22 @@ def load_findings(root: Path) -> list[Finding]:
274274
return sorted(findings, key=lambda f: f.key)
275275

276276

277+
def published_newest_first(findings: list[Finding]) -> list[Finding]:
278+
"""Published findings, most recently disclosed first.
279+
280+
load_findings returns key order, which is the canonical order for detail
281+
pages and duplicate detection but reads as arbitrary in a table: CVE ids
282+
sort lexically, so a 2026 identifier assigned before disclosure can land
283+
above a finding disclosed months later. Both tables want the reverse-
284+
chronological order the talks and links lists already use.
285+
286+
The sort is stable, so same-day disclosures keep the key order they came
287+
in with rather than shuffling between runs and churning the README.
288+
"""
289+
published = [f for f in findings if f.status == "published"]
290+
return sorted(published, key=lambda f: f.disclosed, reverse=True)
291+
292+
277293
def load_talks(root: Path) -> list[Talk]:
278294
talks = []
279295
for path in sorted((root / "talks").glob("*/talk.md")):

tools/research/render_html.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@
1515
import markdown
1616

1717
from .clock import days_remaining, effective_deadline
18-
from .records import Finding, Link, Profile, Talk, ValidationError, id_url
18+
from .records import (
19+
Finding,
20+
Link,
21+
Profile,
22+
Talk,
23+
ValidationError,
24+
id_url,
25+
published_newest_first,
26+
)
1927

2028
TEMPLATES = Path(__file__).parent / "templates"
2129
SOON_THRESHOLD_DAYS = 14
@@ -88,9 +96,7 @@ def _talks_cell(finding: Finding, talk_events: dict[str, str]) -> str:
8896
def _published_rows(findings: list[Finding], talk_events: dict[str, str] | None = None) -> str:
8997
events = talk_events or {}
9098
rows = []
91-
for f in findings:
92-
if f.status != "published":
93-
continue
99+
for f in published_newest_first(findings):
94100
rows.append(
95101
"<tr>"
96102
f'<td class="id"><a href="{_esc(f.key)}/">{_esc(f.id)}</a></td>'

tools/research/render_md.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import re
1010

1111
from .clock import effective_deadline
12-
from .records import Finding, ValidationError, id_url
12+
from .records import Finding, ValidationError, id_url, published_newest_first
1313

1414
MARKER_NAMES = ("published", "pending")
1515

@@ -71,9 +71,7 @@ def _links_cell(finding: Finding, talk_events: dict[str, str]) -> str:
7171
def published_table(findings: list[Finding], talk_events: dict[str, str] | None = None) -> str:
7272
events = talk_events or {}
7373
rows = []
74-
for finding in findings:
75-
if finding.status != "published":
76-
continue
74+
for finding in published_newest_first(findings):
7775
rows.append(
7876
f"| [{_cell(finding.id)}]({_url(id_url(finding.id))}) "
7977
f"| {finding.disclosed:%Y-%m-%d} "

tools/tests/test_records.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,33 @@ def test_load_findings_includes_duplicates_dir_and_sorts(tmp_path):
211211
assert [f.key for f in found] == ["CVE-2025-63662", "CVE-2025-63663", "EL-2026-014"]
212212

213213

214+
def test_published_newest_first_orders_by_disclosure_date(tmp_path):
215+
write(tmp_path, "findings/CVE-2025-63662/finding.md", PUBLISHED)
216+
write(tmp_path, "findings/CVE-2026-27959/finding.md", PUBLISHED
217+
.replace("CVE-2025-63662", "CVE-2026-27959")
218+
.replace("disclosed: 2025-12-22", "disclosed: 2026-02-26"))
219+
ordered = records.published_newest_first(records.load_findings(tmp_path))
220+
assert [f.key for f in ordered] == ["CVE-2026-27959", "CVE-2025-63662"]
221+
222+
223+
def test_published_newest_first_breaks_ties_on_key(tmp_path):
224+
"""Same-day disclosures keep load_findings' key order, so output is stable."""
225+
write(tmp_path, "findings/CVE-2025-63664/finding.md", PUBLISHED.replace("63662", "63664"))
226+
write(tmp_path, "findings/CVE-2025-63662/finding.md", PUBLISHED)
227+
write(tmp_path, "findings/CVE-2025-63663/finding.md", PUBLISHED.replace("63662", "63663"))
228+
ordered = records.published_newest_first(records.load_findings(tmp_path))
229+
assert [f.key for f in ordered] == [
230+
"CVE-2025-63662", "CVE-2025-63663", "CVE-2025-63664",
231+
]
232+
233+
234+
def test_published_newest_first_drops_unpublished_records(tmp_path):
235+
write(tmp_path, "findings/CVE-2025-63662/finding.md", PUBLISHED)
236+
write(tmp_path, "findings/EL-2026-001/finding.md", PENDING)
237+
ordered = records.published_newest_first(records.load_findings(tmp_path))
238+
assert [f.key for f in ordered] == ["CVE-2025-63662"]
239+
240+
214241
def test_duplicate_key_across_findings_and_duplicates_is_an_error(tmp_path):
215242
write(tmp_path, "findings/EL-2026-014/finding.md", PENDING.replace(
216243
"EL-2026-001", "EL-2026-014"))

tools/tests/test_render_html.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ def test_index_published_table_has_seven_columns(tmp_path):
2929
assert column in html
3030

3131

32+
def test_index_published_table_lists_newest_disclosure_first(tmp_path):
33+
older = finding(key="CVE-2025-63662", id="CVE-2025-63662",
34+
disclosed=date(2025, 12, 22))
35+
newer = finding(key="CVE-2026-27959", id="CVE-2026-27959",
36+
disclosed=date(2026, 2, 26))
37+
html = render(tmp_path, findings=[older, newer])
38+
assert html.index("CVE-2026-27959") < html.index("CVE-2025-63662")
39+
40+
3241
def test_index_pending_table_uses_endor_column_names(tmp_path):
3342
html = render(tmp_path)
3443
assert "Project / Repository" in html

tools/tests/test_render_md.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,19 @@ def test_trailing_backslash_in_project_does_not_escape_the_delimiter():
137137
assert len(re.findall(r"(?<!\\)\|", row)) == 6
138138

139139

140+
def test_published_table_lists_newest_disclosure_first():
141+
"""load_findings hands over key order; the table must re-sort by date."""
142+
older = finding(key="CVE-2025-63662", id="CVE-2025-63662",
143+
disclosed=date(2025, 12, 22))
144+
newer = finding(key="CVE-2026-27959", id="CVE-2026-27959",
145+
disclosed=date(2026, 2, 26))
146+
rows = render_md.published_table([older, newer]).splitlines()[2:]
147+
assert [row.split("|")[1].strip() for row in rows] == [
148+
"[CVE-2026-27959](https://nvd.nist.gov/vuln/detail/CVE-2026-27959)",
149+
"[CVE-2025-63662](https://nvd.nist.gov/vuln/detail/CVE-2025-63662)",
150+
]
151+
152+
140153
def test_paren_in_url_does_not_truncate_the_link():
141154
table = render_md.published_table([finding(blog="https://e.com/a)b")])
142155
assert "[Writeup](https://e.com/a%29b)" in table

0 commit comments

Comments
 (0)