Skip to content

Fix Path Traversal: 管理画面ファイル操作のパストラバーサル脆弱性を修正#1342

Merged
nanasess merged 4 commits intomasterfrom
fix/security-path-traversal
Feb 20, 2026
Merged

Fix Path Traversal: 管理画面ファイル操作のパストラバーサル脆弱性を修正#1342
nanasess merged 4 commits intomasterfrom
fix/security-path-traversal

Conversation

@nobuhiko
Copy link
Contributor

@nobuhiko nobuhiko commented Feb 12, 2026

Summary

  • CSS設定: checkPath() を強化し、../ を含むファイル名をブロック
  • ページ詳細設定: createPHPFile()realpath() + str_starts_with() でディレクトリ境界チェックを追加
  • テンプレートアップロード: アーカイブ展開前にパストラバーサル・絶対パス・シンボリックリンクを検証
  • ファイルマネージャ: convertToAbsolutePath()str_contains()str_starts_with() に修正し、パス境界チェックを厳格化

Test plan

  • PHPUnit全テスト通過(MySQL & PostgreSQL)
  • E2Eテスト全テスト通過(MySQL & PostgreSQL)
  • 管理画面のCSS編集が正常動作すること
  • 管理画面のページ詳細設定が正常動作すること
  • テンプレートアップロードが正常動作すること
  • ファイルマネージャが正常動作すること

Refs #1337

🤖 Generated with Claude Code

Summary by CodeRabbit

  • バグ修正
    • ファイルパス検証を強化し、基準ディレクトリ外のパスを拒否するようにしました
    • 管理画面のパス入力検証を改良し、空入力やパストラバーサル表現を適切に判定するようにしました
    • PHP生成処理で早期にパスの安全性を確認するようにしました
    • アップロードされたアーカイブの各エントリを検査し、絶対パス・親ディレクトリ参照・シンボリックリンクを検出すると中止するようにしました
    • 検証完了後に一時ファイルを作成する順序に変更しました

- CSS editor: strengthen checkPath() to block ".." and "/" in filenames
- Page editor: add realpath validation in createPHPFile() to ensure files
  stay within USER_REALDIR
- Template upload: validate archive contents before extraction (block path
  traversal, absolute paths, and symlinks)
- FileManager: fix convertToAbsolutePath() to use str_starts_with() instead
  of str_contains() for secure path boundary check

Refs #1337

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

複数のファイル管理・テンプレート操作でパストラバーサル防止の検証を追加・強化しました。パスの正規化とUSER_REALDIRプレフィックス確認、CSS名の検証強化、アップロードアーカイブ内の相対/絶対パスやシンボリックリンク検出を実装し、不正なパスを早期にブロックします。

Changes

Cohort / File(s) Summary
ファイルマネージャーユーティリティ
data/class/helper/SC_Helper_FileManager.php
convertToAbsolutePath の検証を包含チェックから先頭一致チェックへ変更。realpath(USER_REALDIR) を基準に正規化し、範囲外のパスは USER_REALDIR にリセットします。
デザイン管理: CSS パス検証
data/class/pages/admin/design/LC_Page_Admin_Design_CSS.php
checkPath に空文字許容と明示的なパストラバーサル防止(.., ./, / の判定)を追加。action() の確認・削除分岐で checkPath($this->css_name) を必須化しました。
デザイン管理: PHP ファイル作成検証
data/class/pages/admin/design/LC_Page_Admin_Design_MainEdit.php
createPHPFile で対象パスを realpath で正規化し、USER_REALDIR プレフィックスを満たさない場合は早期に false を返すガードを追加しました。
デザイン管理: アップロード検査順序と安全性
data/class/pages/admin/design/LC_Page_Admin_Design_UpDown.php
アップロードされた tar アーカイブの各エントリを事前検査:.. 含有、先頭 /(絶対パス)、タイプフラグ '2'(シンボリックリンク)を検出した場合はエラーを設定。全検査通過後に makeTempFile を実行するよう順序を変更しました。

Estimated code review effort

🎯 3 (中程度) | ⏱️ ~20 minutes

Suggested reviewers

  • nanasess

Poem

🐰 パスを見張り、穴を塞ぎ
ユーザーの庭はもう安全さ
アーカイブもひとつずつ覗き
絶対も相対も許さぬように
ぽかぽか守る、ぴょんと跳ねる

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Pull request title clearly summarizes the main objective: fixing path traversal vulnerabilities in administrative file operations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/security-path-traversal

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.40%. Comparing base (934da43) to head (37209df).
⚠️ Report is 5 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1342   +/-   ##
=======================================
  Coverage   54.39%   54.40%           
=======================================
  Files          84       84           
  Lines       10815    10816    +1     
=======================================
+ Hits         5883     5884    +1     
  Misses       4932     4932           
Flag Coverage Δ
tests 54.40% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
data/class/pages/admin/design/LC_Page_Admin_Design_CSS.php (1)

298-310: ⚠️ Potential issue | 🔴 Critical

checkPath の検証ロジックは適切ですが、doRegister / doDelete の前に呼ばれていません

checkPath は Line 133 でファイル読み込み前にのみ呼ばれていますが、confirm モード(Line 97-112)と delete モード(Line 113-124)では checkPath による検証なしに $css_path$css_dir.$this->css_name.'.css')を使ってファイル書き込み・削除が行われています。

攻撃者が css_name../../etc/config のような値を送信した場合、doRegisterdoDelete でパストラバーサルが可能になります。

🔒 修正案: confirm/delete の前に checkPath を追加
         switch ($this->getMode()) {
             // データ更新処理
             case 'confirm':
-                if (!$is_error) {
+                if (!$is_error && $this->checkPath($this->css_name)) {
                     $this->arrErr = array_merge($this->arrErr, $this->lfCheckError($objFormParam, $this->arrErr));
             case 'delete':
-                if (!$is_error) {
+                if (!$is_error && $this->checkPath($this->css_name)) {
                     if ($this->doDelete($css_path)) {

Based on learnings: "Check file operations for パストラバーサル and upload validation vulnerabilities"

🤖 Fix all issues with AI agents
In `@data/class/helper/SC_Helper_FileManager.php`:
- Around line 525-527: The prefix check using str_starts_with allows
directory-boundary bypass because realpath(USER_REALDIR) lacks a trailing slash;
in SC_Helper_FileManager.php adjust the check around $path and USER_REALDIR so
you compare with the base directory plus a directory separator (e.g., ensure
$base = rtrim(realpath(USER_REALDIR), DIRECTORY_SEPARATOR) .
DIRECTORY_SEPARATOR) and use that $base in str_starts_with or equivalent — also
normalize $path via realpath() before checking and fall back to $base when the
check fails to prevent paths like /var/www/user_data_evil from passing.

In `@data/class/pages/admin/design/LC_Page_Admin_Design_MainEdit.php`:
- Around line 375-380: The check using realpath(USER_REALDIR) + str_starts_with
can false-positive on similarly named dirs and fails when dirname($path) does
not exist; fix by ensuring USER_REALDIR has a trailing directory separator
before prefix checks (e.g. $userRealDir = rtrim(realpath(USER_REALDIR),
DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR) and use strict prefix check against
that value; if realpath(dirname($path)) returns false, construct a logical
absolute parent path by joining the canonical USER_REALDIR with
dirname($filename) (or use the existing
SC_Helper_FileManager::convertToAbsolutePath logic) and normalize path segments
(resolving "." and ".." without requiring the filesystem) before performing the
same trailing-slash prefix check; update both
LC_Page_Admin_Design_MainEdit::(the method containing $path/$filename checks)
and SC_Helper_FileManager::convertToAbsolutePath accordingly.

nobuhiko and others added 2 commits February 12, 2026 16:00
- str_starts_withのディレクトリ境界バイパス対策(末尾/付加)
- CSS設定のconfirm/deleteモードにcheckPath検証を追加

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@nanasess nanasess left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Found 1 issue:

  1. confirm モードで old_css_name に対する checkPath() が適用されていません。css_name のみ検証されていますが、doRegister() 内で old_css_name が直接 unlink() に使用されるため、攻撃者が css_name に正常な値、old_css_name にパストラバーサルを含む値を送信することで、任意の .css ファイルを削除可能です。

case 'confirm':
if (!$is_error && $this->checkPath($this->css_name)) {
$this->arrErr = array_merge($this->arrErr, $this->lfCheckError($objFormParam, $this->arrErr));

doRegister() 内の該当箇所:

{
if (!SC_Utils_Ex::isBlank($old_css_name)
&& $old_css_name != $css_name) {
if (!unlink($css_dir.$old_css_name.'.css')) {
$this->arrErr['err'] = '※ 旧ファイルの削除に失敗しました<br />';
return false;
}
}

suggested fix:

-                if (!$is_error && $this->checkPath($this->css_name)) {
+                if (!$is_error && $this->checkPath($this->css_name) && $this->checkPath($this->old_css_name)) {

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@nanasess
Copy link
Contributor

上記の old_css_name の件を除き、その他の修正内容は妥当と判断しました。

レビュー観点

以下の3つの観点でレビューを実施しました。

  1. 修正内容の適切性: パストラバーサル対策として十分か、バイパス可能な漏れがないか
  2. 下位互換性: 既存の正常な操作に影響を与えないか
  3. DocumentRoot 以外へのインストール: サブディレクトリにインストールした場合(例: /var/www/html/shop/ に html ディレクトリを配置)でも正しく動作するか

各ファイルの評価

  • SC_Helper_FileManager.php: str_containsstr_starts_with + トレイリングスラッシュ付き比較によるディレクトリ境界チェックの厳格化は適切
  • LC_Page_Admin_Design_MainEdit.php: createPHPFile() での realpath() + USER_REALDIR 境界チェック追加は適切。サブディレクトリ未存在時に realpath()false を返す点も、もともとファイル作成自体が失敗するため回帰なし
  • LC_Page_Admin_Design_UpDown.php: アーカイブ内容の事前検証(..、絶対パス、シンボリックリンク)は Zip Slip 攻撃への適切な対策
  • LC_Page_Admin_Design_CSS.php: confirm/delete モードへの checkPath() 追加、および checkPath() 自体の強化は適切
  • 下位互換性: 正常な操作に影響する変更はなし
  • DocumentRoot 以外へのインストール: 全て realpath() で解決された絶対パスベースの比較のため、サブディレクトリインストールでも問題なし

🤖 Generated with Claude Code

Copy link
Contributor

@nanasess nanasess left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nobuhiko
以下のコメントを確認お願いします🙇‍♂️
#1342 (review)

doRegister() で old_css_name を使ったファイル削除が行われるため、
パストラバーサル防止のために checkPath() による検証を追加。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nanasess nanasess enabled auto-merge February 20, 2026 02:31
Copy link
Contributor

@nanasess nanasess left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
多層防御の観点から有効ですのでマージします

@nanasess nanasess merged commit f5e0dc0 into master Feb 20, 2026
505 of 507 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments