@marimo-team/islands 0.23.2-dev39 → 0.23.2-dev40

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.
@@ -33,7 +33,7 @@ import { a as parser, i as pythonLanguage, n as localCompletionSource, r as pyth
33
33
  import { n as stexMath } from "./stex-Ze8D4R_5.js";
34
34
  import { t as purify } from "./purify.es-ukiMXY-F.js";
35
35
  import { t as useAsyncData } from "./useAsyncData-B1v_9k4L.js";
36
- let createActions$1, isUninstantiated, MarimoIncomingMessageEvent, Trash2, PathBuilder, DotFilledIcon, Database, variablesAtom, contextToXml, Anchor2, getValidName, AccordionContent, Root2$2, customPythonLanguageSupport, Accordion, Item, Checkbox, BorderAllIcon, Layers, generateUUID, base64ToDataView, esm_default, require_client, PinRightIcon, CircleX, PluralWords, ChatBubbleIcon, Info, useChromeActions, extractBase64FromDataURL, getTableType, base64ToUint8Array, import_lib$1, allTablesAtom, CheckIcon, File, repl, MarimoValueReadyEvent, PaintRoller, moveToEndOfEditor, MarimoValueUpdateEvent, NotebookPen, goToCellLine, AccordionItem, Trigger2, MarkdownLanguageAdapter, createInputEvent, LoaderCircle, DeferredRequestRegistry, cellErrorsAtom, useRequestClient, ChevronDownIcon, FileText, adaptForLocalStorage, Sections, selectAtom, displayCellName, AIContextRegistry, Content2$1, normalizeName, deserializeBlob, CircleAlert, DATA_TYPE_ICON, PinLeftIcon, Columns2, PluralWord, base64ToDataURL, Braces, getDataTypeColor, safeExtractSetUIElementMessageBuffers, getRequestClient, isDataURLString, convertStatsName, renderHTML, requestClientAtom, useExpandedConsoleOutput, jsonParseWithSpecialChar, isErrorMime, toPng$1, ZodLocalStorage, reducer$1, getDatasourceContext, atomWithReducer, DATA_CELL_ID, useCellFocusActions, parseDataset, useCellNames, Spinner, HTMLCellId, getInitialAppMode, singleFacet, getCellEditorView, Popover, SCRATCH_CELL_ID, initialModeAtom, isOutputEmpty, RANDOM_ID_ATTR, outputIsStale, PopoverTrigger, OBJECT_ID_ATTR, outputIsLoading, PythonIcon, NotebookScopedLocalStorage, numColumnsAtom, MarimoValueInputEvent, Table2, Paths, AccordionTrigger, Wrench, createVariableInfoElement, useLastFocusedCellId, parseInitialValue, createCell, PopoverContent, findCellId, viewStateAtom, blobToString, extractAllTracebackInfo, notebookAtom, MarkdownRenderer, filenameAtom, useCellActions, PopoverClose, UIElementId, kioskModeAtom, dataViewToBase64, dataSourceConnectionsAtom, filesToBase64, getTracebackInfo, notebookOutline, LazyAnyLanguageCodeMirror, parseAttrValue, useCellIds, processOutput, elementContainsMarimoCellFile, getCellNames, maybeAddAltairImport, CellOutputId, AnsiUp, useExpandedOutput, jsonToMarkdown, headingToIdentifier, AIContextProvider, Close$1, isInternalCellName, Boosts, atomWithStorage, getCellDomProps, DatasourceContextProvider, jsonToTSV, sanitizeHtml, ChevronRightIcon, Eye, jotaiJsonStorage;
36
+ let cellErrorsAtom, useRequestClient, MarimoIncomingMessageEvent, Wrench, createVariableInfoElement, DotFilledIcon, Eye, jotaiJsonStorage, contextToXml, selectAtom, displayCellName, AccordionContent, Item, Checkbox, Accordion, Content2$1, normalizeName, BorderAllIcon, LoaderCircle, DeferredRequestRegistry, base64ToDataView, Braces, getDataTypeColor, PinRightIcon, Columns2, PluralWord, ChatBubbleIcon, Layers, generateUUID, extractBase64FromDataURL, dataSourceConnectionsAtom, base64ToUint8Array, esm_default, require_client, CheckIcon, Info, useChromeActions, MarimoValueReadyEvent, Table2, Paths, MarimoValueUpdateEvent, PaintRoller, moveToEndOfEditor, AccordionItem, Root2$2, customPythonLanguageSupport, createInputEvent, NotebookPen, goToCellLine, hasRunAnyCellAtom, requestClientAtom, ChevronDownIcon, File, repl, Sections, atomWithStorage, getCellDomProps, AIContextRegistry, Close$1, isInternalCellName, deserializeBlob, CircleX, PluralWords, PinLeftIcon, Database, variablesAtom, base64ToDataURL, CircleAlert, DATA_TYPE_ICON, safeExtractSetUIElementMessageBuffers, convertStatsName, isDataURLString, getTableType, renderHTML, getRequestClient, useExpandedConsoleOutput, RANDOM_ID_ATTR, outputIsStale, toPng$1, NotebookScopedLocalStorage, numColumnsAtom, getDatasourceContext, jsonToTSV, sanitizeHtml, useCellFocusActions, parseAttrValue, useCellIds, Spinner, CellOutputId, AnsiUp, isUninstantiated, createActions$1, Popover, HTMLCellId, getInitialAppMode, isOutputEmpty, OBJECT_ID_ATTR, outputIsLoading, PopoverTrigger, findCellId, viewStateAtom, PythonIcon, getTracebackInfo, notebookOutline, MarimoValueInputEvent, Trash2, PathBuilder, AccordionTrigger, Trigger2, MarkdownLanguageAdapter, useLastFocusedCellId, parseDataset, useCellNames, PopoverContent, UIElementId, kioskModeAtom, blobToString, elementContainsMarimoCellFile, getCellNames, MarkdownRenderer, ZodLocalStorage, reducer$1, PopoverClose, SCRATCH_CELL_ID, initialModeAtom, dataViewToBase64, import_lib$1, allTablesAtom, filesToBase64, extractAllTracebackInfo, notebookAtom, LazyAnyLanguageCodeMirror, filenameAtom, useCellActions, processOutput, singleFacet, getCellEditorView, maybeAddAltairImport, parseInitialValue, createCell, useExpandedOutput, jsonParseWithSpecialChar, isErrorMime, AIContextProvider, Anchor2, getValidName, Boosts, atomWithReducer, DATA_CELL_ID, DatasourceContextProvider, jsonToMarkdown, headingToIdentifier, ChevronRightIcon, FileText, adaptForLocalStorage;
37
37
  let __tla = Promise.all([
38
38
  (() => {
39
39
  try {
@@ -26747,7 +26747,7 @@ ${n.sqlString}
26747
26747
  }), t[4] = r, t[5] = a, t[6] = o, t[7] = s) : s = t[7], s;
26748
26748
  };
26749
26749
  require_compiler_runtime();
26750
- const hasRunAnyCellAtom = atom(false);
26750
+ hasRunAnyCellAtom = atom(false);
26751
26751
  var sanitizeHtmlAtom = atom((e) => {
26752
26752
  let t = e(hasRunAnyCellAtom), n = e(autoInstantiateAtom);
26753
26753
  if (t || n) return false;
@@ -28424,159 +28424,160 @@ ${t}
28424
28424
  };
28425
28425
  });
28426
28426
  export {
28427
- createActions$1 as $,
28428
- isUninstantiated as $t,
28427
+ cellErrorsAtom as $,
28428
+ useRequestClient as $t,
28429
28429
  MarimoIncomingMessageEvent as A,
28430
- Trash2 as An,
28431
- PathBuilder as At,
28430
+ Wrench as An,
28431
+ createVariableInfoElement as At,
28432
28432
  DotFilledIcon as B,
28433
- Database as Bn,
28434
- variablesAtom as Bt,
28433
+ Eye as Bn,
28434
+ jotaiJsonStorage as Bt,
28435
28435
  contextToXml as C,
28436
- Anchor2 as Cn,
28437
- getValidName as Ct,
28436
+ selectAtom as Cn,
28437
+ displayCellName as Ct,
28438
28438
  AccordionContent as D,
28439
- Root2$2 as Dn,
28440
- customPythonLanguageSupport as Dt,
28439
+ Item as Dn,
28440
+ Checkbox as Dt,
28441
28441
  Accordion as E,
28442
- Item as En,
28443
- Checkbox as Et,
28442
+ Content2$1 as En,
28443
+ normalizeName as Et,
28444
28444
  BorderAllIcon as F,
28445
- Layers as Fn,
28446
- generateUUID as Ft,
28445
+ LoaderCircle as Fn,
28446
+ DeferredRequestRegistry as Ft,
28447
28447
  base64ToDataView as G,
28448
- esm_default as Gn,
28449
- require_client as Gt,
28448
+ Braces as Gn,
28449
+ getDataTypeColor as Gt,
28450
28450
  PinRightIcon as H,
28451
- CircleX as Hn,
28452
- PluralWords as Ht,
28451
+ Columns2 as Hn,
28452
+ PluralWord as Ht,
28453
28453
  ChatBubbleIcon as I,
28454
- Info as In,
28455
- useChromeActions as It,
28454
+ Layers as In,
28455
+ generateUUID as It,
28456
28456
  extractBase64FromDataURL as J,
28457
- getTableType as Jt,
28457
+ dataSourceConnectionsAtom as Jt,
28458
28458
  base64ToUint8Array as K,
28459
- import_lib$1 as Kn,
28460
- allTablesAtom as Kt,
28459
+ esm_default as Kn,
28460
+ require_client as Kt,
28461
28461
  CheckIcon as L,
28462
- File as Ln,
28463
- repl as Lt,
28462
+ Info as Ln,
28463
+ useChromeActions as Lt,
28464
28464
  MarimoValueReadyEvent as M,
28465
- PaintRoller as Mn,
28466
- moveToEndOfEditor as Mt,
28465
+ Table2 as Mn,
28466
+ Paths as Mt,
28467
28467
  MarimoValueUpdateEvent as N,
28468
- NotebookPen as Nn,
28469
- goToCellLine as Nt,
28468
+ PaintRoller as Nn,
28469
+ moveToEndOfEditor as Nt,
28470
28470
  AccordionItem as O,
28471
- Trigger2 as On,
28472
- MarkdownLanguageAdapter as Ot,
28471
+ Root2$2 as On,
28472
+ customPythonLanguageSupport as Ot,
28473
28473
  createInputEvent as P,
28474
- LoaderCircle as Pn,
28475
- DeferredRequestRegistry as Pt,
28476
- cellErrorsAtom as Q,
28477
- useRequestClient as Qt,
28474
+ NotebookPen as Pn,
28475
+ goToCellLine as Pt,
28476
+ hasRunAnyCellAtom as Q,
28477
+ requestClientAtom as Qt,
28478
28478
  ChevronDownIcon as R,
28479
- FileText as Rn,
28480
- adaptForLocalStorage as Rt,
28479
+ File as Rn,
28480
+ repl as Rt,
28481
28481
  Sections as S,
28482
- selectAtom as Sn,
28483
- displayCellName as St,
28482
+ atomWithStorage as Sn,
28483
+ getCellDomProps as St,
28484
28484
  AIContextRegistry as T,
28485
- Content2$1 as Tn,
28486
- normalizeName as Tt,
28485
+ Close$1 as Tn,
28486
+ isInternalCellName as Tt,
28487
28487
  deserializeBlob as U,
28488
- CircleAlert as Un,
28489
- DATA_TYPE_ICON as Ut,
28488
+ CircleX as Un,
28489
+ PluralWords as Ut,
28490
28490
  PinLeftIcon as V,
28491
- Columns2 as Vn,
28492
- PluralWord as Vt,
28491
+ Database as Vn,
28492
+ variablesAtom as Vt,
28493
28493
  base64ToDataURL as W,
28494
- Braces as Wn,
28495
- getDataTypeColor as Wt,
28494
+ CircleAlert as Wn,
28495
+ DATA_TYPE_ICON as Wt,
28496
28496
  safeExtractSetUIElementMessageBuffers as X,
28497
- getRequestClient as Xt,
28497
+ convertStatsName as Xt,
28498
28498
  isDataURLString as Y,
28499
- convertStatsName as Yt,
28499
+ getTableType as Yt,
28500
28500
  renderHTML as Z,
28501
- requestClientAtom as Zt,
28501
+ getRequestClient as Zt,
28502
28502
  useExpandedConsoleOutput as _,
28503
28503
  __tla,
28504
- jsonParseWithSpecialChar as _n,
28505
- isErrorMime as _t,
28504
+ RANDOM_ID_ATTR as _n,
28505
+ outputIsStale as _t,
28506
28506
  toPng$1 as a,
28507
- ZodLocalStorage as an,
28508
- reducer$1 as at,
28507
+ NotebookScopedLocalStorage as an,
28508
+ numColumnsAtom as at,
28509
28509
  getDatasourceContext as b,
28510
- atomWithReducer as bn,
28511
- DATA_CELL_ID as bt,
28510
+ jsonToTSV as bn,
28511
+ sanitizeHtml as bt,
28512
28512
  useCellFocusActions as c,
28513
- parseDataset as cn,
28514
- useCellNames as ct,
28513
+ parseAttrValue as cn,
28514
+ useCellIds as ct,
28515
28515
  Spinner as d,
28516
- HTMLCellId as dn,
28517
- getInitialAppMode as dt,
28518
- singleFacet as en,
28519
- getCellEditorView as et,
28516
+ CellOutputId as dn,
28517
+ AnsiUp as dt,
28518
+ isUninstantiated as en,
28519
+ createActions$1 as et,
28520
28520
  Popover as f,
28521
- SCRATCH_CELL_ID as fn,
28522
- initialModeAtom as ft,
28521
+ HTMLCellId as fn,
28522
+ getInitialAppMode as ft,
28523
28523
  isOutputEmpty as g,
28524
- RANDOM_ID_ATTR as gn,
28525
- outputIsStale as gt,
28524
+ OBJECT_ID_ATTR as gn,
28525
+ outputIsLoading as gt,
28526
28526
  PopoverTrigger as h,
28527
- OBJECT_ID_ATTR as hn,
28528
- outputIsLoading as ht,
28527
+ findCellId as hn,
28528
+ viewStateAtom as ht,
28529
28529
  PythonIcon as i,
28530
- NotebookScopedLocalStorage as in,
28531
- numColumnsAtom as it,
28530
+ getTracebackInfo as in,
28531
+ notebookOutline as it,
28532
28532
  MarimoValueInputEvent as j,
28533
- Table2 as jn,
28534
- Paths as jt,
28533
+ Trash2 as jn,
28534
+ PathBuilder as jt,
28535
28535
  AccordionTrigger as k,
28536
- Wrench as kn,
28537
- createVariableInfoElement as kt,
28536
+ Trigger2 as kn,
28537
+ MarkdownLanguageAdapter as kt,
28538
28538
  useLastFocusedCellId as l,
28539
- parseInitialValue as ln,
28540
- createCell as lt,
28539
+ parseDataset as ln,
28540
+ useCellNames as lt,
28541
28541
  PopoverContent as m,
28542
- findCellId as mn,
28543
- viewStateAtom as mt,
28542
+ UIElementId as mn,
28543
+ kioskModeAtom as mt,
28544
28544
  blobToString as n,
28545
- extractAllTracebackInfo as nn,
28546
- notebookAtom as nt,
28545
+ elementContainsMarimoCellFile as nn,
28546
+ getCellNames as nt,
28547
28547
  MarkdownRenderer as o,
28548
- filenameAtom as on,
28549
- useCellActions as ot,
28548
+ ZodLocalStorage as on,
28549
+ reducer$1 as ot,
28550
28550
  PopoverClose as p,
28551
- UIElementId as pn,
28552
- kioskModeAtom as pt,
28551
+ SCRATCH_CELL_ID as pn,
28552
+ initialModeAtom as pt,
28553
28553
  dataViewToBase64 as q,
28554
- dataSourceConnectionsAtom as qt,
28554
+ import_lib$1 as qn,
28555
+ allTablesAtom as qt,
28555
28556
  filesToBase64 as r,
28556
- getTracebackInfo as rn,
28557
- notebookOutline as rt,
28557
+ extractAllTracebackInfo as rn,
28558
+ notebookAtom as rt,
28558
28559
  LazyAnyLanguageCodeMirror as s,
28559
- parseAttrValue as sn,
28560
- useCellIds as st,
28560
+ filenameAtom as sn,
28561
+ useCellActions as st,
28561
28562
  processOutput as t,
28562
- elementContainsMarimoCellFile as tn,
28563
- getCellNames as tt,
28563
+ singleFacet as tn,
28564
+ getCellEditorView as tt,
28564
28565
  maybeAddAltairImport as u,
28565
- CellOutputId as un,
28566
- AnsiUp as ut,
28566
+ parseInitialValue as un,
28567
+ createCell as ut,
28567
28568
  useExpandedOutput as v,
28568
- jsonToMarkdown as vn,
28569
- headingToIdentifier as vt,
28569
+ jsonParseWithSpecialChar as vn,
28570
+ isErrorMime as vt,
28570
28571
  AIContextProvider as w,
28571
- Close$1 as wn,
28572
- isInternalCellName as wt,
28572
+ Anchor2 as wn,
28573
+ getValidName as wt,
28573
28574
  Boosts as x,
28574
- atomWithStorage as xn,
28575
- getCellDomProps as xt,
28575
+ atomWithReducer as xn,
28576
+ DATA_CELL_ID as xt,
28576
28577
  DatasourceContextProvider as y,
28577
- jsonToTSV as yn,
28578
- sanitizeHtml as yt,
28578
+ jsonToMarkdown as yn,
28579
+ headingToIdentifier as yt,
28579
28580
  ChevronRightIcon as z,
28580
- Eye as zn,
28581
- jotaiJsonStorage as zt
28581
+ FileText as zn,
28582
+ adaptForLocalStorage as zt
28582
28583
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.23.2-dev39",
3
+ "version": "0.23.2-dev40",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -1,5 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import { describe, expect, it } from "vitest";
2
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
3
+ import { hasRunAnyCellAtom } from "@/components/editor/cell/useRunCells";
4
+ import { store } from "@/core/state/jotai";
3
5
  import { isTrustedVirtualFileUrl } from "../trusted-url";
4
6
 
5
7
  describe("isTrustedVirtualFileUrl", () => {
@@ -45,4 +47,46 @@ describe("isTrustedVirtualFileUrl", () => {
45
47
  expect(isTrustedVirtualFileUrl(42)).toBe(false);
46
48
  expect(isTrustedVirtualFileUrl({})).toBe(false);
47
49
  });
50
+
51
+ describe("data URL exemption", () => {
52
+ let previousHasRunAnyCell: boolean;
53
+
54
+ beforeEach(() => {
55
+ previousHasRunAnyCell = store.get(hasRunAnyCellAtom);
56
+ store.set(hasRunAnyCellAtom, true);
57
+ });
58
+
59
+ afterEach(() => {
60
+ store.set(hasRunAnyCellAtom, previousHasRunAnyCell);
61
+ });
62
+
63
+ it.each([
64
+ "data:text/javascript;base64,ZXhwb3J0IGRlZmF1bHQge30=",
65
+ "data:application/javascript;base64,ZXhwb3J0IGRlZmF1bHQge30=",
66
+ "data:text/css;base64,Ym9keXt9",
67
+ ])("accepts safe data URL %s after a cell has run", (url) => {
68
+ expect(isTrustedVirtualFileUrl(url)).toBe(true);
69
+ });
70
+
71
+ it.each([
72
+ // Non-base64 data URLs are refused
73
+ "data:text/javascript,alert(1)",
74
+ "data:text/javascript;charset=utf-8,alert(1)",
75
+ // HTML / SVG / arbitrary types are refused
76
+ "data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==",
77
+ "data:image/svg+xml;base64,PHN2Zy8+",
78
+ "data:application/octet-stream;base64,AAA=",
79
+ ])("still rejects unsafe data URL %s", (url) => {
80
+ expect(isTrustedVirtualFileUrl(url)).toBe(false);
81
+ });
82
+
83
+ it("rejects data URL when no cell has been run", () => {
84
+ store.set(hasRunAnyCellAtom, false);
85
+ expect(
86
+ isTrustedVirtualFileUrl(
87
+ "data:text/javascript;base64,ZXhwb3J0IGRlZmF1bHQge30=",
88
+ ),
89
+ ).toBe(false);
90
+ });
91
+ });
48
92
  });
@@ -1,20 +1,45 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import { hasRunAnyCellAtom } from "@/components/editor/cell/useRunCells";
4
+ import { store } from "@/core/state/jotai";
5
+
3
6
  /**
4
7
  * Whether a URL can be trusted to point at a marimo-served virtual file.
5
8
  *
6
9
  * Plugins that load remote scripts or stylesheets (e.g. MplInteractive, Panel)
7
10
  * must call this before turning a plugin-supplied URL into a `<script src>` or
8
- * `<link href>`. The backend always serializes these URLs as virtual file
11
+ * `<link href>`. The backend normally serializes these URLs as virtual file
9
12
  * paths of the form `./@file/<byte_length>-<filename>` (see
10
13
  * `VirtualFile.create_and_register`). Accepting anything else would let a
11
14
  * maliciously crafted `<marimo-*>` element embedded in markdown load
12
15
  * attacker-controlled JavaScript at same origin, since the HTML sanitizer
13
16
  * lets arbitrary marimo custom elements and attributes through.
17
+ *
18
+ * Some runtimes (WASM, VS Code) have no backend to serve virtual files, so
19
+ * `VirtualFile` falls back to inline base64 data URLs (see `virtual_file.py`).
20
+ * We accept those only once the user has explicitly run a cell in the current
21
+ * notebook — the same trust signal `sanitize.ts` uses to lift HTML
22
+ * sanitization. Running a cell requires deliberate user action and already
23
+ * executes arbitrary Python, so a data URL script loaded afterwards is not a
24
+ * new attack surface.
14
25
  */
15
26
  export function isTrustedVirtualFileUrl(url: unknown): url is string {
16
27
  if (typeof url !== "string" || url.length === 0) {
17
28
  return false;
18
29
  }
19
- return /^(\.?\/)?@file\/[^?#]+$/.test(url);
30
+ if (/^(\.?\/)?@file\/[^?#]+$/.test(url)) {
31
+ return true;
32
+ }
33
+ if (isSafeDataUrl(url) && store.get(hasRunAnyCellAtom)) {
34
+ return true;
35
+ }
36
+ return false;
37
+ }
38
+
39
+ function isSafeDataUrl(url: string): boolean {
40
+ return (
41
+ url.startsWith("data:text/javascript;base64,") ||
42
+ url.startsWith("data:application/javascript;base64,") ||
43
+ url.startsWith("data:text/css;base64,")
44
+ );
20
45
  }