# 機能設計書 72-パスサニタイズ

## 概要

本ドキュメントは、Etherpadにおけるパスサニタイズ機能の設計を定義する。ファイルパスやURLパスに対するセキュリティ検証とサニタイズ処理について記述する。

### 本機能の処理概要

ユーザー入力やリクエストパスに含まれる潜在的に危険なパス文字列を検証・正規化し、ディレクトリトラバーサル攻撃などのセキュリティ脆弱性を防ぐ機能である。

**業務上の目的・背景**：Webアプリケーションにおいて、ユーザーが入力したパス情報を直接使用すると、ディレクトリトラバーサル攻撃（CVE-2015-3297等）により、意図しないファイルへのアクセスが発生する可能性がある。本機能により、そのような攻撃を未然に防ぎ、システムのセキュリティを確保する。

**機能の利用シーン**：
- 静的ファイル配信時のファイルパス検証
- パッドIDの正規化とサニタイズ
- プラグインパスの検証
- インポート/エクスポート機能でのファイル名検証

**主要な処理内容**：
1. パスの正規化（path.normalize()によるパス文字列の標準化）
2. 絶対パスの検出と拒否（相対パスのみ許可）
3. ディレクトリトラバーサル（..）の検出と拒否
4. Windowsパス区切り文字のフォワードスラッシュへの変換
5. パッドIDの有効性検証とサニタイズ

**関連システム・外部連携**：Node.js標準のpathモジュールを使用。Express.jsミドルウェアとして動作。

**権限による制御**：全てのリクエストに対して自動的に適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パッド編集画面 | 主画面 | パッドURLのサニタイズ処理 |
| 3 | タイムスライダー画面 | 参照画面 | パッドURLのサニタイズ処理 |

## 機能種別

バリデーション / セキュリティ処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pathname | string | Yes | サニタイズ対象のパス文字列 | 空文字列でないこと |
| pathApi | object | No | Node.jsのpathモジュール互換オブジェクト | デフォルトはpath |
| padId | string | Yes（URL用） | パッドの識別子 | 有効なパッドID形式であること |

### 入力データソース

- HTTPリクエストURL
- ファイルシステムパス
- ユーザー入力のパッドID

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| sanitizedPath | string | サニタイズ済みの安全なパス |
| isValid | boolean | パッドIDが有効かどうか |
| redirectUrl | string | リダイレクト先URL（サニタイズ後のパッドID用） |

### 出力先

- 正規化されたパス文字列を返却
- 無効なパスの場合はErrorをスロー
- パッドURLの場合は302リダイレクトまたは次のミドルウェアへ

## 処理フロー

### 処理シーケンス

```
1. パス正規化
   └─ path.normalize()で '..' や '.' を解決
2. 絶対パスチェック
   └─ path.isAbsolute()で絶対パスを検出しエラー
3. ディレクトリトラバーサルチェック
   └─ パス先頭が '..' で始まる場合はエラー
4. Windowsパス変換
   └─ バックスラッシュをフォワードスラッシュに変換
5. サニタイズ済みパスを返却
```

### フローチャート

```mermaid
flowchart TD
    A[パス入力] --> B[path.normalize実行]
    B --> C{絶対パス?}
    C -->|Yes| D[Error: absolute paths are forbidden]
    C -->|No| E{先頭が '..'?}
    E -->|Yes| F[Error: directory traversal]
    E -->|No| G{Windowsパス?}
    G -->|Yes| H[バックスラッシュ変換]
    G -->|No| I[サニタイズ済みパス返却]
    H --> I
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-72-01 | 絶対パス禁止 | 絶対パスは許可しない | path.isAbsolute()がtrueの場合 |
| BR-72-02 | 親ディレクトリ禁止 | '..'による親ディレクトリ参照を禁止 | 正規化後のパス先頭が'..'の場合 |
| BR-72-03 | パス区切り統一 | Windowsパス区切りをPOSIX形式に統一 | Windowsプラットフォームの場合 |
| BR-72-04 | パッドID正規化 | 不正なパッドIDは正規化してリダイレクト | パッドIDが有効でサニタイズ後に変更がある場合 |
| BR-72-05 | 無効パッドID拒否 | 無効なパッドIDは404エラー | isValidPadId()がfalseの場合 |

### 計算ロジック

特になし

## データベース操作仕様

本機能ではデータベース操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | 絶対パス検出 | "absolute paths are forbidden: {path}" |
| - | Error | ディレクトリトラバーサル検出 | "directory traversal: {path}" |
| 404 | HTTP Error | 無効なパッドID | "Such a padname is forbidden" |

### リトライ仕様

なし

## トランザクション仕様

なし

## パフォーマンス要件

- パス検証は同期的に実行され、ミリ秒単位で完了すること
- 全てのリクエストに対して実行されるため、軽量な処理であること

## セキュリティ考慮事項

- CVE-2015-3297（ディレクトリトラバーサル脆弱性）への対策
- シンボリックリンクの追跡は行わない（path.normalize()の仕様による）
- Windows/POSIXの両方のパス形式に対応
- ユーザー入力を直接ファイルシステムパスとして使用しない

## 備考

- sanitizePathname関数は静的ファイル配信（Minify）で使用
- padurlsanitize.tsはExpressミドルウェアとしてパッドURLを処理
- PadManager.sanitizePadId()はパッドIDの正規化に使用

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

パスサニタイズに関する入力データ形式を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sanitizePathname.ts | `src/node/utils/sanitizePathname.ts` | サニタイズ関数の引数と戻り値の型 |

**読解のコツ**: Node.jsのpathモジュールのAPIを理解しておくと、normalize、isAbsolute、sepの動作が分かりやすい。

#### Step 2: エントリーポイントを理解する

パスサニタイズが呼び出される場所を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Minify.ts | `src/node/utils/Minify.ts` | 静的ファイル配信時のパスサニタイズ呼び出し |
| 2-2 | padurlsanitize.ts | `src/node/hooks/express/padurlsanitize.ts` | パッドURLサニタイズのExpressミドルウェア |

**主要処理フロー**:
1. **Minify.ts 150-158行目**: ファイルパスをsanitizePathname()で検証
2. **padurlsanitize.ts 9-31行目**: パッドIDのサニタイズとリダイレクト処理

#### Step 3: サニタイズ実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sanitizePathname.ts | `src/node/utils/sanitizePathname.ts` | サニタイズ処理の実装詳細 |

**主要処理フロー**:
- **5-21行目**: sanitizeRoot関数の完全な実装
- **11行目**: path.normalize()による正規化
- **12行目**: 絶対パスチェック
- **13行目**: ディレクトリトラバーサルチェック
- **19行目**: Windowsパス区切りの変換

#### Step 4: パッドID検証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PadManager.ts | `src/node/db/PadManager.ts` | パッドIDの有効性検証 |

**主要処理フロー**:
- isValidPadId()関数: パッドIDの形式検証
- sanitizePadId()関数: パッドIDの正規化

### プログラム呼び出し階層図

```
HTTPリクエスト
    │
    ├─ padurlsanitize.ts (Expressミドルウェア)
    │      ├─ padManager.isValidPadId()
    │      │      └─ パッドID形式の検証
    │      └─ padManager.sanitizePadId()
    │             └─ パッドIDの正規化
    │
    └─ Minify.ts (_minify関数)
           └─ sanitizePathname()
                  ├─ path.normalize()
                  ├─ path.isAbsolute() チェック
                  └─ '..' 先頭チェック
```

### データフロー図

```
[入力]                     [処理]                      [出力]

/p/../../etc/passwd ───▶ sanitizePathname() ───▶ Error: directory traversal
                               │
/p/TestPad ─────────────▶ padurlsanitize ─────▶ /p/testpad (302リダイレクト)
                               │
/static/../../../etc ───▶ sanitizePathname() ───▶ Error: directory traversal
                               │
/static/js/pad.js ──────▶ sanitizePathname() ───▶ "js/pad.js" (正規化パス)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sanitizePathname.ts | `src/node/utils/sanitizePathname.ts` | ソース | パスサニタイズのコア実装 |
| padurlsanitize.ts | `src/node/hooks/express/padurlsanitize.ts` | ソース | パッドURLサニタイズのExpressミドルウェア |
| Minify.ts | `src/node/utils/Minify.ts` | ソース | 静的ファイル配信でサニタイズを使用 |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | パッドIDの検証・正規化 |
| sanitizePathname.ts | `src/tests/backend-new/specs/sanitizePathname.ts` | テスト | サニタイズ機能のテストケース |
