jamdesk 1.1.93 → 1.1.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jamdesk",
3
- "version": "1.1.93",
3
+ "version": "1.1.94",
4
4
  "description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
5
5
  "keywords": [
6
6
  "jamdesk",
@@ -34,6 +34,20 @@ export function readCache(source: string): CachedDiagram | null {
34
34
  if (!raw) return null;
35
35
  const parsed = JSON.parse(raw) as Partial<CachedDiagram>;
36
36
  if (typeof parsed?.svg !== 'string') return null;
37
+ // Defense-in-depth: mermaid sanitizes at render time (securityLevel:
38
+ // 'strict'), but the cache-read path injects the stored bytes via React's
39
+ // raw inner-HTML prop without re-sanitizing. A tampered sessionStorage
40
+ // entry must not be trusted — reject anything that isn't a bare <svg> root
41
+ // or that carries a <script> or an inline on*= event handler. On rejection
42
+ // we return null so the caller falls back to a fresh, re-sanitized render.
43
+ const svg = parsed.svg.trimStart();
44
+ if (
45
+ !svg.startsWith('<svg') ||
46
+ /<script\b/i.test(svg) ||
47
+ /<[^>]+\son[a-z]+\s*=/i.test(svg)
48
+ ) {
49
+ return null;
50
+ }
37
51
  // Normalize the shape so the return honestly matches CachedDiagram:
38
52
  // legacy v1 entries predate height tracking, and a tampered/foreign
39
53
  // entry could carry a non-numeric height. Coerce both to a number.
@@ -52,12 +66,19 @@ export function writeCache(source: string, entry: CachedDiagram): void {
52
66
  } catch {}
53
67
  }
54
68
 
55
- // Mermaid always emits a `height` attribute on the root <svg>. Reading it
56
- // from the markup (rather than getBoundingClientRect) works in jsdom and
57
- // avoids a forced reflow on the production hot path.
69
+ // Real mermaid output does NOT carry a `height` attribute it sizes the root
70
+ // <svg> via `viewBox` + `style="max-width"`. So read the explicit `height`
71
+ // attribute first (covers other renderers / older mermaid), then fall back to
72
+ // the 4th `viewBox` token (its height). Parsing the markup rather than
73
+ // getBoundingClientRect works in jsdom and avoids a forced reflow on the
74
+ // production hot path.
58
75
  export function readSvgHeight(svgMarkup: string): number {
59
- const match = svgMarkup.match(/<svg\b[^>]*\bheight=["']([\d.]+)(?:px)?["']/i);
60
- return match ? parseFloat(match[1]) : 0;
76
+ const attr = svgMarkup.match(/<svg\b[^>]*\bheight=["']([\d.]+)(?:px)?["']/i);
77
+ if (attr) return parseFloat(attr[1]);
78
+ const viewBox = svgMarkup.match(
79
+ /<svg\b[^>]*\bviewBox=["']\s*[\d.+-]+\s+[\d.+-]+\s+[\d.+-]+\s+([\d.]+)/i
80
+ );
81
+ return viewBox ? parseFloat(viewBox[1]) : 0;
61
82
  }
62
83
 
63
84
  interface MermaidInnerProps {