DOMPurify <=3.3.1 (and the mermaid-transitive 3.3.3) carry ~18 disclosed
sanitizer-bypass/XSS advisories, including GHSA-cj63-jhhr-wcxv
(CVE-2026-65913): with USE_PROFILES enabled, ALLOWED_ATTR is rebuilt as a
plain array and looked up via ALLOWED_ATTR[lcName], so a polluted
Array.prototype property (e.g. onclick) is treated as an allow-listed
attribute and survives sanitization -- this app calls
DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true, svgFilters: true } })
in src/utils/sanitize.ts, whose output is rendered via v-html in
ImageUpload.vue's SVG upload preview.
Bumped to 3.4.14 (latest, OSV-clean) and pinned via pnpm.overrides so the
mermaid-transitive copy dedupes to the same patched version instead of
staying pinned at 3.3.3. Lockfile-only regen via pnpm 9, no other package
changes.
- Add prompt_audit_events.full_prompt (migration 182) so admins can review
the exact unredacted prompt that triggered a finding; blocking mode writes
it from the snapshot, async mode reconstructs it from the Redis scan
payload so jobs rows stay redaction-only
- Event detail API returns full_prompt (list endpoint stays lean); text is
NUL-stripped and capped at 65536 runes
- Detail dialog shows the full prompt in a scrollable pane with fallback to
the legacy redacted preview; page copy updated to match the new behavior
- Rework filter deletion into a dedicated dialog with time-range presets and
criteria-change preview invalidation; localize decision/risk/category
labels across the events workspace
- Fix pre-existing i18n message-compile spec by declaring the
@intlify/message-compiler dev dependency
Resolves GHSA-hmw2-7cc7-3qxx (CRLF injection) flagged by
frontend-security CI. axios pulls form-data ^4.0.5 which locked to the
vulnerable 4.0.5; override forces all transitive consumers to 4.0.6+
without needing an audit exception.
Add a full payment and subscription system supporting EasyPay (Alipay/WeChat),
Stripe, and direct Alipay/WeChat Pay providers with multi-instance load balancing.
Replace direct row rendering with @tanstack/vue-virtual. The table
now only renders visible rows (~20) via padding <tr> placeholders,
eliminating the rendering bottleneck when displaying 100+ rows with
heavy cell components.
Key changes:
- DataTable.vue: integrate useVirtualizer (always-on), virtual row
template with measureElement for variable row heights, defineExpose
virtualizer/sortedData for external access, overflow-y/flex CSS
- useSwipeSelect.ts: dual-mode support via optional
SwipeSelectVirtualContext — data-driven row index lookup and
selection range when virtualizer is present, original DOM-based
path preserved for callers that don't pass virtualContext
- Add `sort_order` field to groups table with migration
- Add `PUT /api/v1/admin/groups/sort-order` API for batch update
- Implement drag-and-drop UI using vue-draggable-plus
- All queries now order groups by sort_order
- Add i18n support (en/zh) for sort-related UI text
- Update test stubs to satisfy new interface methods