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.
The admin user edit modal rejected concurrency < 1, so a user whose
concurrency is already 0 could not be saved at all — the guard runs
before the request, blocking notes, password, role and RPM edits on that
user too.
Everywhere else already treats 0 as unlimited: the gateway skips slot
limiting when maxConcurrency <= 0 (ConcurrencyService.AcquireUserSlot),
the batch limits endpoint binds concurrency with min=0, and the bulk edit
modal only rejects negative values.
Reject negative and non-integer values instead, mirror the RPM field with
min/step and a "0 = unlimited" placeholder and hint, and rename the error
key to match its new meaning. Account concurrency is unchanged.
The quick-add parser rejected every IPv6 proxy: the host group [^:]+
cannot match IPv6 literals (colons) and the pattern had no bracketed
form, so lines like socks5://[2001:db8::1]:1080 were reported invalid.
Add a bracketed-IPv6 host alternative and strip the brackets before
storing; the backend re-brackets via net.JoinHostPort when building the
proxy URL. Bare (unbracketed) IPv6 stays rejected because it is
ambiguous with host:port. Also add a regression test.
The model plaza route already supports public access, but both built-in /home headers omit its entry. Add the link to compact and default headers while keeping the existing feature and authentication settings authoritative, then cover the visibility matrix with focused component tests.
Constraint: Keep the change frontend-only and preserve router-owned access control
Rejected: Add the link to AppHeader only | /home renders its own headers and never mounts AppHeader
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep the model plaza entry gated by the existing opt-in flag and require-auth setting
Tested: HomeView focused Vitest, full frontend Vitest (223 files / 1554 tests), ESLint, vue-tsc, production build
Not-tested: Manual browser click-through against a running backend
Related: #5524
The quota cell used the noun label "5-hour window/weekly window" and the
balance cell the balance value itself as the click target for a manual
refresh. Both read as passive captions, so users could not discover the
manual probe and reported the feature as missing ("only OpenAI has a
refresh button").
- Split data display from the refresh action: bars/balance render as
static rows (snapshot already paints on mount), with a dedicated
action button below, aligned with the OpenAI "Query" and Grok
"Probe" buttons (same icon, blue action styling).
- Label the control with a verb (cnProviders.probe: 查询 / Query) and
give the balance cell a tooltip (it previously had none).
- Keep the tier/label layout classes and data-test hooks intact.
- Extend the quota cell spec and add a balance cell spec covering
snapshot rendering, explicit action labeling and failure passthrough.