diff --git a/apps/jupyterlab/4.6.2/.env.sample b/apps/jupyterlab/4.6.2/.env.sample new file mode 100644 index 000000000..40b46cfed --- /dev/null +++ b/apps/jupyterlab/4.6.2/.env.sample @@ -0,0 +1,5 @@ +PANEL_APP_PORT_HTTP=8888 +JUPYTER_TOKEN= +APP_DATA_DIR=./data +TZ=Asia/Shanghai +CONTAINER_NAME= diff --git a/apps/jupyterlab/4.6.2/data.yml b/apps/jupyterlab/4.6.2/data.yml new file mode 100644 index 000000000..9725405d2 --- /dev/null +++ b/apps/jupyterlab/4.6.2/data.yml @@ -0,0 +1,69 @@ +additionalProperties: + formFields: + - default: 8888 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Port + labelZh: 端口 + label: + en: Port + zh: 端口 + zh-Hant: 埠 + ja: ポート + ko: 포트 + ru: Порт + ms: Port + pt-br: Porta + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: JUPYTER_TOKEN + labelEn: Access Token + labelZh: 访问令牌 + label: + en: Access Token + zh: 访问令牌 + zh-Hant: 存取權杖 + ja: アクセストークン + ko: 액세스 토큰 + ru: Токен доступа + ms: Token akses + pt-br: Token de acesso + random: true + required: true + rule: paramComplexity + type: password + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Work Directory + labelZh: 工作目录 + label: + en: Work Directory + zh: 工作目录 + zh-Hant: 工作目錄 + ja: 作業ディレクトリ + ko: 작업 디렉터리 + ru: Рабочий каталог + ms: Direktori kerja + pt-br: Diretório de trabalho + required: true + type: text + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon waktu + pt-br: Fuso horário + required: true + type: text diff --git a/apps/jupyterlab/4.6.2/data/.gitkeep b/apps/jupyterlab/4.6.2/data/.gitkeep new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apps/jupyterlab/4.6.2/data/.gitkeep @@ -0,0 +1 @@ + diff --git a/apps/jupyterlab/4.6.2/docker-compose.yml b/apps/jupyterlab/4.6.2/docker-compose.yml new file mode 100644 index 000000000..c4195520d --- /dev/null +++ b/apps/jupyterlab/4.6.2/docker-compose.yml @@ -0,0 +1,28 @@ +services: + jupyterlab: + image: "quay.io/jupyter/base-notebook:lab-4.6.2@sha256:50d8c29bb555af7ba18e956c97734c31ada26ee12d7e6dc77927608cb730d108" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1000:100" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:8888" + command: + - start-notebook.py + - --IdentityProvider.token=${JUPYTER_TOKEN} + - --ServerApp.allow_remote_access=True + environment: + - TZ=${TZ} + security_opt: + - no-new-privileges:true + cap_drop: + - ALL + volumes: + - "${APP_DATA_DIR}:/home/jovyan/work" + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/jupyterlab/4.6.2/scripts/init.sh b/apps/jupyterlab/4.6.2/scripts/init.sh new file mode 100755 index 000000000..4e4c62f9e --- /dev/null +++ b/apps/jupyterlab/4.6.2/scripts/init.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local key="$1" + [[ -f "$ENV_FILE" ]] || return 0 + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +configured_value() { + local key="$1" + local default_value="$2" + local value="${!key:-}" + if [[ -z "$value" ]]; then + value="$(read_env_value "$key")" + fi + printf '%s\n' "${value:-$default_value}" +} + +prepare_work_dir() { + local raw path + raw="$(configured_value APP_DATA_DIR ./data)" + + [[ -n "$raw" ]] || { + printf '%s\n' 'APP_DATA_DIR must not be empty' >&2 + exit 1 + } + if [[ "$raw" = /* ]]; then + printf '%s\n' 'APP_DATA_DIR must be relative to the application version directory' >&2 + exit 1 + fi + + path="$(realpath -m -- "$ROOT_DIR/${raw#./}")" + case "$path" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2 + exit 1 + ;; + esac + + install -d -m 0750 "$path" + path="$(realpath -e -- "$path")" + case "$path" in + "$ROOT_DIR"/*) ;; + *) + printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2 + exit 1 + ;; + esac + chmod 0750 "$path" + chown -R --no-dereference 1000:100 "$path" +} + +prepare_work_dir diff --git a/apps/jupyterlab/4.6.2/scripts/uninstall.sh b/apps/jupyterlab/4.6.2/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/jupyterlab/4.6.2/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/jupyterlab/4.6.2/scripts/upgrade.sh b/apps/jupyterlab/4.6.2/scripts/upgrade.sh new file mode 100755 index 000000000..692e341e2 --- /dev/null +++ b/apps/jupyterlab/4.6.2/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +"$(dirname "$0")/init.sh" diff --git a/apps/jupyterlab/README.md b/apps/jupyterlab/README.md new file mode 100644 index 000000000..c2de8ef4a --- /dev/null +++ b/apps/jupyterlab/README.md @@ -0,0 +1,60 @@ +# JupyterLab + +## 产品介绍 + +JupyterLab 是 Project Jupyter 的 Web 开发环境,可在浏览器中编辑和运行 notebook、代码、终端及数据文件。 + +## 主要功能 + +- 创建、编辑和运行 Jupyter Notebook +- 集成文本编辑器、终端、文件浏览器和交互式内核 +- 使用标签页和分栏组织多个文档及计算任务 + +## 访问说明 + +安装后通过 `http://<服务器 IP>:<端口>` 访问,实际端口以 `PANEL_APP_PORT_HTTP` 为准。登录时使用安装表单生成或填写的访问令牌。生产环境应通过可信反向代理提供 HTTPS,避免令牌和 notebook 内容通过明文网络传输。 + +JupyterLab 是单用户服务,不提供多用户隔离。获得令牌的用户可以在容器内执行任意代码、打开终端并读写工作目录,因此只能向完全可信的用户开放。不要导入或打开来源不明的 notebook;执行前应检查代码单元、Markdown、富输出和扩展内容。 + +## 数据持久化 + +`APP_DATA_DIR` 挂载到 `/home/jovyan/work`,保存 notebook 和工作文件。该路径必须位于应用版本目录内,默认值为 `./data`;初始化脚本会拒绝绝对路径和目录外路径,并设置为官方镜像用户 UID `1000`、GID `100`。卸载不会删除绑定目录中的用户数据,升级或迁移前请单独备份。 + +## 安全与部署风险 + +- 容器以官方 `jovyan` 用户(UID `1000`、GID `100`)运行,丢弃全部 Linux capabilities,并启用 `no-new-privileges`。没有启用 sudo 或 Docker Socket。 +- 候选来源的 JupyterLab 4.6.1 扫描曾报告 `6` 个 High;本包固定到官方 `lab-4.6.2` 镜像后,`GHSA-gx64-gj6p-pc4c`(图片查看器 XSS)和 `GHSA-pppj-hq3g-57pj`(`overrides.json` XSS)已由 JupyterLab 4.6.2 修复。以下 `4` 项是对最终固定镜像重新扫描后的剩余风险。 +- 固定镜像的 2026-07-28 Trivy 扫描发现 `0` 个 Critical 和 `4` 个 High。`CVE-2023-39663`(MathJax ReDoS)和 `CVE-2026-27601`(Underscore 递归结构拒绝服务)位于浏览器侧依赖;打开恶意 notebook 或富内容可能消耗浏览器 CPU/内存。只处理可信文件,并在修复版镜像可用后尽快更新。 +- `GHSA-36hh-v3qg-5jq4`(PyO3 越界读取)和 `GHSA-4w2j-m93h-cj5j`(quinn-proto 远程内存耗尽)位于镜像内 `rattler` 包管理二进制,不在默认 Jupyter HTTP 请求路径中。包安装、环境更新和扩展安装仍应仅连接可信源,并在修复版镜像可用后更新。 + +## Introduction + +JupyterLab is Project Jupyter's web-based development environment for editing and running notebooks, code, terminals, and data files. + +## Features + +- Create, edit, and run Jupyter notebooks +- Use integrated text editors, terminals, file browsers, and interactive kernels +- Organize multiple documents and compute tasks with tabs and split views + +## Usage Notes + +- Access the service at `http://:` and sign in with the access token generated or supplied during installation. Use a trusted HTTPS reverse proxy for production access. +- JupyterLab is a single-user service without multi-user isolation. Anyone with the token can execute arbitrary code, open a terminal, and modify the work directory. Expose it only to fully trusted users and inspect untrusted notebooks, Markdown, rich output, and extensions before opening or running them. +- `APP_DATA_DIR` is mounted at `/home/jovyan/work`. It must remain relative to the application version directory and is prepared for the official UID `1000`, GID `100`. Back it up before upgrades or migration; uninstall does not delete bind-mounted user data. + +## Security and Deployment Risks + +- The container runs as the official non-root `jovyan` user, drops all Linux capabilities, enables `no-new-privileges`, and receives neither sudo nor a container-engine socket. +- The source candidate's JupyterLab 4.6.1 scan reported 6 High findings. Pinning the package to the official `lab-4.6.2` image resolves `GHSA-gx64-gj6p-pc4c` (image-viewer XSS) and `GHSA-pppj-hq3g-57pj` (`overrides.json` XSS), both fixed in JupyterLab 4.6.2. The following 4 findings are the residual risks from rescanning the final pinned image. +- A 2026-07-28 Trivy scan of the pinned image found 0 Critical and 4 High findings. `CVE-2023-39663` (MathJax ReDoS) and `CVE-2026-27601` (Underscore recursive-structure denial of service) affect browser-side dependencies; malicious notebooks or rich content may consume browser CPU or memory. Open trusted files only and update when a fixed image is available. +- `GHSA-36hh-v3qg-5jq4` (PyO3 out-of-bounds read) and `GHSA-4w2j-m93h-cj5j` (quinn-proto remote memory exhaustion) are in the image's `rattler` package-management binary and are not on the default Jupyter HTTP request path. Use trusted package and extension sources and update when fixed builds are available. + +## References + +- Project: +- Official image source: +- Container usage: +- Server security: +- License: (BSD-3-Clause) +- Logo: (BSD-3-Clause) diff --git a/apps/jupyterlab/data.yml b/apps/jupyterlab/data.yml new file mode 100644 index 000000000..ae7f264b1 --- /dev/null +++ b/apps/jupyterlab/data.yml @@ -0,0 +1,30 @@ +name: JupyterLab +tags: + - DevTool +title: 交互式计算与数据科学开发环境 +description: 交互式计算与数据科学开发环境 +additionalProperties: + key: jupyterlab + name: JupyterLab + tags: + - DevTool + shortDescZh: 交互式计算与数据科学开发环境 + shortDescEn: An interactive computing and data science environment + description: + en: An interactive computing and data science environment + zh: 交互式计算与数据科学开发环境 + zh-Hant: 互動式運算與資料科學開發環境 + ja: 対話型コンピューティングとデータサイエンスの開発環境 + ko: 대화형 컴퓨팅 및 데이터 과학 개발 환경 + ru: Интерактивная среда для вычислений и анализа данных + ms: Persekitaran pengkomputeran interaktif dan sains data + pt-br: Ambiente interativo de computação e ciência de dados + type: website + crossVersionUpdate: true + limit: 0 + website: https://jupyterlab.readthedocs.io/en/latest/ + github: https://github.com/jupyterlab/jupyterlab + document: https://jupyter-docker-stacks.readthedocs.io/en/latest/using/common.html + architectures: + - amd64 + - arm64 diff --git a/apps/jupyterlab/logo.png b/apps/jupyterlab/logo.png new file mode 100644 index 000000000..1cf9602f3 Binary files /dev/null and b/apps/jupyterlab/logo.png differ