TL;DR

Wiki.js 2.x ships Mermaid 8.8.2, released in 2020. Mermaid 11 — the current stable release — adds timeline diagrams, improved gitGraph, better theming, and fixes years of rendering bugs. The upstream project defers this upgrade to Wiki.js v3, which has no release date. The PR queue has sat idle for over a year. I forked requarks/wiki at tag v2.5.312, upgraded Mermaid in-place, patched 8 CVEs including one Critical SAML authentication bypass, reduced the vulnerability count from 8 Critical / 48 High to 3 Critical / 42 High, and deployed it to the cluster. The fork stays close to upstream — Vue 2 and Webpack 4 are untouched. Only the Mermaid surface and security dependencies are modified.

The Problem

The cluster’s internal wiki uses Mermaid diagrams for every architecture page. Infrastructure without diagrams is harder to reason about quickly — I want to look at a page and immediately see how traffic flows, not read paragraphs.

Mermaid 8.8.2 is missing:

  • Timeline diagrams — useful for incident post-mortems and project timelines
  • Improved gitGraph — the v8 version barely renders
  • mindmap and quadrantChart — new diagram types I actually use
  • Better error messages — v8 fails silently on syntax errors

When I tried to paste a Mermaid 11 diagram into the wiki, it rendered as blank. The parser rejected the newer syntax without any error message shown to the user.

Why Fork Instead of Wait

The upstream issue has been open since 2023. The maintainers closed a PR that upgraded Mermaid with the explanation that the architecture change required for Mermaid 10+ is reserved for v3. v3 has no ETA and no public roadmap with dates.

Forking is a maintenance burden, but the surface area is small. The Mermaid integration in Wiki.js is isolated — it is a render call in the markdown pipeline with a couple of configuration hooks. The plan is:

  1. Fork at the latest stable tag
  2. Upgrade only Mermaid and security-critical dependencies
  3. Keep Vue 2 and Webpack 4 untouched (touching these opens a migration that is months of work)
  4. Track upstream tags periodically and cherry-pick security patches

The Mermaid Upgrade

The Mermaid integration lives in a few files:

client/components/editor/markdown/mermaid.vue   # renderer component
server/modules/rendering/md-mermaid/             # server-side render module
package.json                                     # mermaid version pin

The upgrade itself was straightforward:

npm install mermaid@^11.13.0

The hard part was fixing the syntax incompatibilities. Mermaid 11 is stricter than v8 in several ways that broke existing diagrams in the wiki:

Issuev8 behaviorv11 behaviorFix
Node ID = subgraph titleRenders (incorrectly)Parse errorRename conflicting node IDs
<br/> in unquoted labelsRendersParse errorQuote all labels
Unclosed code fencesIgnoredTreats remainder as Mermaid inputFix fences in content
> in labelsRendersParse errorUse &#62;

I wrote a Selenium regression suite (tests/wiki-regression/) with 22 tests covering 10 diagram types and both light and dark mode rendering. All 50 SVGs render correctly after the fixes.

✓ flowchart (light + dark)
✓ sequence (light + dark)
✓ class diagram (light + dark)
✓ state diagram (light + dark)
✓ ER diagram (light + dark)
✓ gantt (light + dark)
✓ pie chart (light + dark)
✓ gitGraph (light + dark)
✓ mindmap (light + dark)
✓ timeline (light + dark)

The Security Patches

The fork was also an opportunity to fix CVEs that upstream had not addressed. Trivy scan results before and after:

SeverityBeforeAfter
Critical83
High4842

Fixed:

PackageCVEFix
passport-samlCVE-2025-54419 (auth bypass)Migrated to @node-saml/passport-saml v5.1.0
simple-gitPinned to 3.33.0
body-parserBumped to 1.20.4
@babel/traverseBumped to 7.29.0
underscoreBumped to 1.13.8
json5Bumped to 2.2.3
node-fetchBumped to 2.6.7
xml-cryptoRemoved (v5 of passport-saml uses xml-crypto 6.x natively)

The SAML auth bypass was the most important fix. Wiki.js uses SAML for SSO integrations, and the old passport-saml had a signature validation vulnerability. Even though I use local auth on this deployment, the CVE was Critical and the fix was clean.

Cannot fix:

PackageReason
css-what 4.xLocked by Webpack 4 — requires Webpack 5 migration
braces 2.xSame Webpack 4 lock
postgres Go CVEsWaiting on Alpine base image update from upstream

The remaining 3 Critical are form-data CVSS 5.4 issues — low exploitability in this context.

CI Pipeline

The CI pipeline builds the fork image and pushes to Harbor staging:

- name: Build and push
  uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    platforms: linux/amd64
    provenance: false   # required for k3s containerd
    tags: |
      harbor.k3s.internal.zolty.systems/staging/wiki:latest
      harbor.k3s.internal.zolty.systems/staging/wiki:${{ github.sha }}

The --provenance=false flag is mandatory. Docker Desktop’s default provenance attestation manifests are not resolvable by k3s containerd. Without it, image pulls fail silently.

Cold builds take 35-45 minutes due to native module compilation and Webpack bundling. Layer caching cuts this to 8-12 minutes for incremental changes. Docker Desktop has crashed on builds exceeding 40 minutes — the retry picks up from cached layers.

Deployment

The deployment is straightforward. The fork image swaps directly into the existing Wiki.js manifest:

image: harbor.k3s.internal.zolty.systems/production/wiki:sha-abc1234

Strategy is Recreate — single replica, no rolling update needed. Postgres stays untouched.

Rollback if needed:

kubectl set image deployment/wiki wiki=ghcr.io/requarks/wiki:2.5 -n wiki

Security Headers

While in the manifest, I also added a Traefik middleware for security headers:

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: wiki-security-headers
  namespace: wiki
spec:
  headers:
    customResponseHeaders:
      Strict-Transport-Security: "max-age=31536000; includeSubDomains"
      X-Frame-Options: "DENY"
      X-Content-Type-Options: "nosniff"
      Permissions-Policy: "camera=(), microphone=(), geolocation=()"
    contentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https://static.requarks.io; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'"

CSP for Wiki.js requires unsafe-inline and unsafe-eval — the Vue 2 + Webpack 4 stack does not support strict CSP without a significant refactor. frame-ancestors 'none' at least prevents clickjacking.

Maintenance Plan

Upstream Wiki.js v2 still receives occasional security patches. The plan:

  1. Watch upstream for new tags
  2. git cherry-pick security-relevant commits onto zolty/main
  3. Re-run Trivy scan after each cherry-pick
  4. Run the Selenium regression suite before deploying

If v3 ever ships with Mermaid 11 support and a clean migration path, I will evaluate it then. Until that happens, the fork is the pragmatic choice — the Mermaid upgrade alone justifies the maintenance overhead.