Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
| **Activity readability Phase 4 — grouping / dedup / noise control** | P2 | Fast-follow to the merged Phases 0-3 (`docs/engineering/activity_readability_plan.md` §Phase 4). Collapse bursts (e.g. "12 packages updated on web-01" instead of 12 rows), suppress monitoring flaps (the NULL→online dev-restart noise above is one case), severity rollups, "N similar events". **Most visible target:** the feed + settings audit log are dominated by `scheduler.tick.dispatched` (~7k) and `system.package.installed` (~7k). Design fork to settle: group at query time (backend, scales) vs client-side (page-only) — recommend backend. **Coupled taxonomy question:** should routine `scheduler.tick.dispatched` be an *audit* event at all? It bloats the AU-compliance audit trail; consider demoting it to a non-audit metric/log |
| **Activity readability Phase 5 — audit compliance hardening** | P2 | Fast-follow, committed track (`activity_readability_plan.md` §Phase 5), for the FedRAMP/CMMC/NIST-800-53 **AU** family. (a) **Tamper-evidence**: populate the `signature` column already reserved on `audit_events` (Ed25519 per-event signing or a hash-chain over the log) — AU-9. (b) **Retention/archival** policy for `audit_events` (none today; relate to the host soft-delete sweep below) — AU-11. (c) An explicit **AU-control mapping doc** stating which capability satisfies AU-2/3/6/7/9/12 (export already covers AU-7) |
| Dashboard "Top failing hosts" widget shows a host UUID | P3 | `WidgetTopFailingHosts` (`frontend/src/pages/dashboard/widgets.tsx`) renders `nameOf(h.host_id)` which falls back to a truncated UUID (`019eccd8…`) when the host isn't in the loaded hosts list. Same "no UUIDs" goal as the activity-readability work, but a non-activity widget. Resolve the name (the widget already has, or can fetch, the host list) so it never shows a UUID |
| Cursor pagination drops rows that share the boundary timestamp | P1 | **Pre-existing** (predates rc.11; surfaced by the pre-release review, not a regression from these changes). The activity feed (`internal/activity/service.go:72-75` + `:156-158`) and the audit-events list (`internal/server/handlers.go` queryEvents) encode the cursor as `occurred_at` alone and filter `occurred_at < cursor` with `ORDER BY occurred_at DESC` (audit adds `, id DESC` to the order but NOT the cursor predicate). If the row trimmed at a `limit` boundary shares the same `occurred_at` as the last returned row, it (and any peers at that instant) are **silently skipped on the next page** — real data loss, made likely on the activity feed because the 5-leg UNION + batch inserts (intelligence/monitoring) produce timestamp ties. Both `TestList_CursorPagination` and `TestAPI_AuditEvents_CursorPagination` use unique per-row timestamps, so the tie path is untested. **Fix:** compound cursor `(occurred_at, id)` with a row-value predicate `(occurred_at, id) < ($ts, $id)` and `ORDER BY occurred_at DESC, id DESC`. Non-trivial for the activity feed: the monitoring leg's `id` is synthesized in the SELECT (not WHERE-able), so the compound predicate must be applied in an outer query wrapping the UNION. Add a tie-straddling test (two rows, identical `occurred_at`, across a limit boundary) |

---

Expand Down
56 changes: 56 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,62 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

---

## [0.2.0-rc.12] Eyrie — 2026-06-20

The fleet activity stream and audit trail are now readable end to end: every
event renders a plain-language title instead of a raw dotted code, enum, or
resource UUID. Host detail gains live Activity and Audit tabs, Settings shows
readable audit rows, and the filtered audit trail can be exported to CSV or
JSON (NIST 800-53 AU-7). The Host Management page got its scan link, Group, and
Filters working with a server-persisted view preference, and a pre-release
security pass hardened the new export and fixed a cursor data-loss bug.

### Added

- Activity & audit readability: the unified feed and the audit list now render
a server-built, human-readable title + summary for all five legs. The three
legs that previously leaked machine codes (compliance/transaction,
intelligence, audit) are humanized — a rule's catalog title instead of its
id, "Package updated" instead of `system.package.updated`, "alice@example.com
created a host" instead of `host.created` over a UUID. Unmapped codes degrade
structurally (dots/underscores to spaces) so a newly-added code can never
surface as a raw dotted enum. (#616, #617)
- Host detail: a live **Activity** tab (host-scoped unified feed) and a
readable **Audit log** tab, with audit `message`/`resource` filters so you can
pull one host's lifecycle trail. (#618, #619)
- Settings: a readable **Audit log** view with plain-language rows. (#622)
- Audit export: `GET /api/v1/audit/events/export` streams the filtered audit
trail as a downloadable CSV (default) or JSON attachment, capped at 10000
rows, `audit:read`-gated (NIST 800-53 AU-7). (#623)

### Changed

- Host Management: the host card's scan link now opens the latest scan, Group
and Filters work, and the list/grid view toggle is persisted **per user**
server-side instead of per browser. (#611)
- Scan detail: the header shows the host's hostname (falling back to its IP)
instead of a raw host UUID. (#613)
- Automated, schedule-driven events are now attributed to **"The system"**
instead of the misleading "Someone", which implied a logged-in operator
clicked a button. (#620)

### Security

- Hardened the audit CSV export against spreadsheet formula injection
(CWE-1236): a cell beginning with `=`, `+`, `-`, `@`, tab, or CR is prefixed
with a single quote so it renders as literal text. A truncated export (at the
10000-row cap) now sets an `X-OpenWatch-Export-Truncated` header and logs a
warning, so a capped export is never mistaken for the complete trail. (#625)
- Fixed a cursor-pagination data-loss bug in the activity feed and audit list:
the cursor encoded `occurred_at` alone, so rows sharing a boundary timestamp
could be silently skipped on the next page (likely on the 5-leg UNION with
batch inserts). Both now use a compound keyset cursor `(occurred_at, id)` with
a row-value predicate. Bounded the attacker-controlled User-Agent and
submitted-username strings recorded in audit detail (256-rune cap +
control-char strip), neutralizing log forging. (#626)

---

## [0.2.0-rc.11] Eyrie — 2026-06-19

The bundled Kensa scan engine moves to v0.5.2, which corrects a class of false
Expand Down
2 changes: 1 addition & 1 deletion packaging/version.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
#
# The Go binary's ldflags read this file via the Makefile; build scripts
# in packaging/{rpm,deb}/ source it for spec macros.
VERSION="0.2.0-rc.11"
VERSION="0.2.0-rc.12"
CODENAME="Eyrie"
Loading