Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ export function useNotebookActions() {
{
icon: <MarimoPlusIcon size={14} strokeWidth={1.5} />,
label: "Create molab notebook",
hidden: !sharingWasmEnabled,
handle: async () => {
const code = await readCode();
const url = createShareableLink({
Expand Down
11 changes: 11 additions & 0 deletions marimo/_server/api/endpoints/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,22 @@ async def export_as_html(
type: string
400:
description: File must be saved before downloading
403:
description: HTML sharing is disabled by configuration
"""
app_state = AppState(request)
body = await parse_request(request, cls=ExportAsHTMLRequest)
session = app_state.require_current_session()

if (
session.config_manager.get_config().get("sharing", {}).get("html")
is False
):
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="HTML sharing is disabled by configuration.",
)

# Check if the file is named
if not session.app_file_manager.is_notebook_named:
raise HTTPException(
Expand Down
11 changes: 10 additions & 1 deletion marimo/_server/api/endpoints/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async def read_code(
400:
description: File must be saved before downloading
403:
description: Code is not available in run mode
description: Code is not available in run mode, or sharing is disabled by configuration
"""
app_state = AppState(request)

Expand All @@ -72,6 +72,15 @@ async def read_code(

session = app_state.require_current_session()

if (
session.config_manager.get_config().get("sharing", {}).get("wasm")
is False
):
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN,
detail="Code sharing is disabled by configuration.",
)

if not session.app_file_manager.path:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
Expand Down
25 changes: 25 additions & 0 deletions tests/_server/api/endpoints/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,31 @@ def test_read_code_without_saved_file(client: TestClient) -> None:
)


@with_session(SESSION_ID)
def test_read_code_with_wasm_sharing_disabled(client: TestClient) -> None:
"""Test read_code is blocked when sharing.wasm = false in config."""
with patch("marimo._server.api.endpoints.files.AppState") as mock_state:
mock_session = Mock()
mock_session.config_manager.get_config.return_value = {
"sharing": {"wasm": False}
}
mock_session.app_file_manager.path = "notebook.py"
mock_state.return_value.session_manager.should_send_code_to_frontend.return_value = True
mock_state.return_value.require_current_session.return_value = (
mock_session
)

response = client.post(
"/api/kernel/read_code",
headers=HEADERS,
json={},
)

# handle_error converts all 403s β†’ 401 to prompt browser re-auth
assert response.status_code == 401
assert "Authorization header required" in response.json()["detail"]


@with_session(SESSION_ID)
def test_save_with_unicode_content(client: TestClient) -> None:
"""Test save endpoint with unicode and special characters."""
Expand Down
Loading