@marimo-team/islands 0.23.2-dev38 → 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.
- package/dist/{chat-ui-CqkLwgnX.js → chat-ui-D3oNzud1.js} +2753 -2753
- package/dist/main.js +158 -155
- package/dist/{process-output-Db_3pJA4.js → process-output-BWEipdFR.js} +120 -105
- package/package.json +1 -1
- package/src/core/cells/__tests__/apply-transaction.test.ts +441 -0
- package/src/core/cells/__tests__/cells.test.ts +110 -0
- package/src/core/cells/cells.ts +18 -0
- package/src/core/cells/document-changes.ts +34 -1
- package/src/plugins/core/__test__/trusted-url.test.ts +45 -1
- package/src/plugins/core/trusted-url.ts +27 -2
|
@@ -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
|
|
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
|
-
|
|
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
|
}
|