何を見つけたか
bin/devbase の maybe_cd_project() は、name 候補を $DEVBASE_ROOT/projects/<name> へそのまま連結してディレクトリの存在を見ます。弾いているのは - 始まりと空文字だけで、.. を含む値がそのまま通ります。
maybe_cd_project() {
local name="${1:-}"
case "$name" in -*|"") return 1 ;; esac # フラグ・空は name ではない
local target="${DEVBASE_ROOT}/projects/${name}"
[ -d "$target" ] || return 1
cd "$target" || return 1
$DEVBASE_ROOT/projects/../etc は $DEVBASE_ROOT/etc に解決され、実在するため cd されます。v3.1.0 の実機で確認しました。
$ devbase build ../etc
=== Building devbase images ===
[1/2] devbase-base already exists (use --no-cache to rebuild)
[2/2] Building project image...
no configuration file provided: not found
✗ Failed to build project image
$DEVBASE_ROOT/etc へ移動して compose ビルドを試み、compose.yml が無いため落ちています。引数は name として消費されるため、下流の Python には届きません。
cd の直後に対象ディレクトリの env を source します(maybe_cd_project 内、現プロセスで source)。projects/ の外にある env という名前のファイルも、この経路で読まれます。
どこで見つけたか
bin/devbase の maybe_cd_project()(320-351 行、v3.3.0 + #171 時点)。#139 の修正で _build_single_image に image 名の検証([A-Za-z0-9][A-Za-z0-9._-]*)を入れた後、v3.1.0 のリリース後テストで devbase build ../etc を実行して気づきました。Python 側の検証は name 解決より後段にあるため、この経路には効きません。
なぜこの変更の範囲外なのか
#139 の受け入れ条件は devbase build <image> が正しい docker コマンドを組み立てることで、name 解決の仕組みは対象外です(issues/old/PLAN49_build-image-argument.md の前提 2 と「対象範囲(含まない)」)。この挙動は #139 の修正より前から同じで、今回の変更が持ち込んだものではありません。
直さないと何が起きるか
影響は build だけではありません。name 解決を通るのは次のすべてです。
- トップレベルショートカット:
up / down / ps / scale / login / build / rebuild
project <sub> <name> / container <sub> <name> の up / down / ps / logs / scale / rebuild
いずれも .. を含む値で projects/ の外のディレクトリを対象にできます。打ち間違い(devbase up ../foo)や、name を組み立てて渡すスクリプトが、意図しないディレクトリで compose 操作を行い、そこの env を読みます。
権限の境界を越えるものではありません(利用者が自分の devbase に自分で引数を渡しているだけです)。ただし「projects/<name> の 1 つを指す」という前提が破れており、下流の Python 側だけを検証しても塞げない位置にあります。
対処の候補:
maybe_cd_project の入口で、name を 1 セグメントに限定する(/ \ .. . を弾く)。_build_single_image に入れたのと同じ形の許可リストが使えます
- あわせて
_resolve_project_name(lib/devbase/commands/container.py)も同じ検証を持つ。Python 側の chdir フォールバックも同じ連結を行うため
cli._named_lifecycle_project(lib/devbase/cli.py)も同じ検証を持つ。グループ別の置き場(backend.yml の version: 2)で、名前を指定したライフサイクル操作の dispatch 前の注入先を (root / 'projects' / name).is_dir() で決め、groups.declared_group(root, name) が projects/<name>/env を読む。.. を含む名前では projects/ の外の env を読んだ後に、SecretRef.for_project の検証(プロジェクト名にパス区切りは使えません)で注入が止まる
由来
issue #139
進行
モード: standard / 作業ツリー: .worktrees/fix/plan61-name-resolution / 計画: issues/PLAN61_name-resolution.md
何を見つけたか
bin/devbaseのmaybe_cd_project()は、name 候補を$DEVBASE_ROOT/projects/<name>へそのまま連結してディレクトリの存在を見ます。弾いているのは-始まりと空文字だけで、..を含む値がそのまま通ります。$DEVBASE_ROOT/projects/../etcは$DEVBASE_ROOT/etcに解決され、実在するため cd されます。v3.1.0 の実機で確認しました。$DEVBASE_ROOT/etcへ移動して compose ビルドを試み、compose.yml が無いため落ちています。引数は name として消費されるため、下流の Python には届きません。cd の直後に対象ディレクトリの
envをsourceします(maybe_cd_project内、現プロセスで source)。projects/の外にあるenvという名前のファイルも、この経路で読まれます。どこで見つけたか
bin/devbaseのmaybe_cd_project()(320-351 行、v3.3.0 + #171 時点)。#139 の修正で_build_single_imageに image 名の検証([A-Za-z0-9][A-Za-z0-9._-]*)を入れた後、v3.1.0 のリリース後テストでdevbase build ../etcを実行して気づきました。Python 側の検証は name 解決より後段にあるため、この経路には効きません。なぜこの変更の範囲外なのか
#139 の受け入れ条件は
devbase build <image>が正しいdockerコマンドを組み立てることで、name 解決の仕組みは対象外です(issues/old/PLAN49_build-image-argument.mdの前提 2 と「対象範囲(含まない)」)。この挙動は #139 の修正より前から同じで、今回の変更が持ち込んだものではありません。直さないと何が起きるか
影響は
buildだけではありません。name 解決を通るのは次のすべてです。up/down/ps/scale/login/build/rebuildproject <sub> <name>/container <sub> <name>のup/down/ps/logs/scale/rebuildいずれも
..を含む値でprojects/の外のディレクトリを対象にできます。打ち間違い(devbase up ../foo)や、name を組み立てて渡すスクリプトが、意図しないディレクトリで compose 操作を行い、そこのenvを読みます。権限の境界を越えるものではありません(利用者が自分の devbase に自分で引数を渡しているだけです)。ただし「
projects/<name>の 1 つを指す」という前提が破れており、下流の Python 側だけを検証しても塞げない位置にあります。対処の候補:
maybe_cd_projectの入口で、name を 1 セグメントに限定する(/\...を弾く)。_build_single_imageに入れたのと同じ形の許可リストが使えます_resolve_project_name(lib/devbase/commands/container.py)も同じ検証を持つ。Python 側の chdir フォールバックも同じ連結を行うためcli._named_lifecycle_project(lib/devbase/cli.py)も同じ検証を持つ。グループ別の置き場(backend.ymlのversion: 2)で、名前を指定したライフサイクル操作の dispatch 前の注入先を(root / 'projects' / name).is_dir()で決め、groups.declared_group(root, name)がprojects/<name>/envを読む。..を含む名前ではprojects/の外のenvを読んだ後に、SecretRef.for_projectの検証(プロジェクト名にパス区切りは使えません)で注入が止まる由来
issue #139
進行
モード: standard / 作業ツリー:
.worktrees/fix/plan61-name-resolution/ 計画:issues/PLAN61_name-resolution.md