# セキュリティ設計書

## 概要

本ドキュメントは、Clojure-LSP (Language Server Protocol) のセキュリティ設計を定義する。Clojure-LSPはClojure/ClojureScriptのコード解析・補完・リファクタリング機能を提供するLSPサーバー実装であり、エディタ/IDEからの接続を受け付けてコード分析結果を返す。本システムは信頼されたローカル環境での動作を前提としており、LSPプロトコル仕様に準拠した設計となっている。

## 認証設計

### 認証方式

Clojure-LSPはLSPプロトコル仕様に準拠しており、明示的な認証層は実装していない。LSPプロトコル自体に認証機構が定義されていないため、以下の信頼モデルを採用している。

**信頼モデル**:
- ローカルプロセス間通信（stdio）またはローカルソケット通信を前提
- 親プロセス（エディタ/IDE）との信頼関係に依存
- クライアントからのプロセスID監視による接続管理

**実装箇所**: `cli/src/clojure_lsp/server.clj` (lines 549-570)

```clojure
(defmethod jsonrpc.server/receive-request "initialize" [_ {:keys [db* server] :as components} params]
  (logger/info startup/logger-tag "Initializing...")
  (handler/initialize components
                      (some-> (:root-uri params) normalize-uri)
                      (:capabilities params)
                      (client-settings params)
                      (:client-info params)
                      (some-> params :work-done-token str))
  (when-let [parent-process-id (:process-id params)]
    (jsonrpc.liveness-probe/start! parent-process-id log-wrapper-fn #(exit server)))
  {:capabilities (capabilities (settings/all @db*))})
```

### セッション管理

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| セッション有効期限 | プロセス終了まで | LSPサーバーのライフサイクルに依存 |
| セッション固定化対策 | N/A | ステートレスRPC方式のため該当なし |
| 接続監視 | 親プロセスID監視 | 親プロセス終了時に自動シャットダウン |
| 同時接続数 | 1（単一クライアント） | 1つのエディタインスタンスに対応 |

## 認可設計

### 権限体系

Clojure-LSPは単一ユーザー・ローカル実行モデルのため、ロールベースのアクセス制御は実装していない。すべての操作はLSPプロトコルで定義されたcapabilitiesによって制御される。

| ロール | 権限 | 説明 |
| --- | --- | --- |
| LSPクライアント | 全LSP操作 | 初期化時にネゴシエートされたcapabilitiesの範囲内で操作可能 |

### アクセス制御

**Capabilityベースのアクセス制御**:
- LSP初期化時にクライアントとサーバー間でcapabilitiesをネゴシエート
- サーバーは自身がサポートする機能のみを公開
- クライアントからの未サポート操作リクエストは拒否

**ファイルシステムアクセス制御**:
- ワークスペースルート配下のファイルのみを分析対象
- 外部ファイル（JAR内、ワークスペース外）は識別・区別される
- ファイルの直接書き込みは行わない（編集はクライアント経由）

**実装箇所**: `lib/src/clojure_lsp/shared.clj` (lines 291-297)

```clojure
(defn external-filename? [filename source-paths]
  (boolean
    (and filename
         (when-not (string/ends-with? filename (str ".lsp" fs/file-separator "config.edn"))
           (or (-> filename name jar-file?)
               (and (seq source-paths)
                    (not (seq (filename->source-paths filename source-paths)))))))))
```

## 通信セキュリティ

| 項目 | 対策 |
| --- | --- |
| HTTPS | 外部依存関係取得時に使用（Clojars等）。HttpsURLConnectionによるSSL/TLS |
| HSTS | N/A（外部サーバーへの接続のみ、自身はHTTPサーバーではない） |
| LSP通信 | stdio（標準入出力）またはローカルソケット。ネットワーク露出なし |
| タイムアウト | HTTP接続: 1秒、HTTP読み込み: 30秒 |

**HTTP通信実装**: `lib/src/clojure_lsp/http.clj`

```clojure
(def ^:private default-connect-timeout-ms 1000)
(def ^:private default-read-timeout-ms 30000)

(defn request! [^String url]
  (try
    (let [url (URL. url)
          conn ^HttpsURLConnection (.openConnection url)]
      (.setConnectTimeout conn default-connect-timeout-ms)
      (.setReadTimeout conn default-read-timeout-ms)
      {:status (.getResponseCode conn)
       :body (.getInputStream conn)
       :content-type (.getContentType conn)})
    (catch Exception ex
      {:error ex})))
```

## データセキュリティ

### 暗号化

| 対象 | 暗号化方式 |
| --- | --- |
| 通信 | HTTPS（TLS 1.2+、Java標準実装） - 外部サーバーとの通信時のみ |
| 保存データ | 暗号化なし - キャッシュファイルはTransit JSON形式で平文保存 |
| LSP通信 | 暗号化なし - ローカルプロセス間通信のため |

### 機密情報管理

**設定ファイル**:
- グローバル設定: `~/.config/clojure-lsp/config.edn` または `~/.lsp/config.edn`
- プロジェクト設定: `.lsp/config.edn`
- XDG Base Directory仕様に準拠

**キャッシュ管理**:
- キャッシュディレクトリ: `~/.cache/clojure-lsp/`
- プロジェクトごとにサブディレクトリで分離
- キャッシュにはコード分析結果（シンボル情報等）を保存

**実装箇所**: `lib/src/clojure_lsp/config.clj` (lines 31-44)

```clojure
(defn global-config-file []
  (let [xdg-config-home (or (get-env "XDG_CONFIG_HOME")
                            (io/file (get-property "user.home") ".config"))
        xdg-config (io/file xdg-config-home "clojure-lsp")
        home-config (io/file (get-property "user.home") ".lsp")
        path (if (shared/file-exists? xdg-config)
               xdg-config
               home-config)]
    (io/file path "config.edn")))
```

**ログ出力**:
- ログファイル: プロジェクトルート配下の `.lsp/.cache/` または設定ディレクトリ
- クライアント情報、バージョン、OS情報をログに記録
- ホスト名は空文字列でマスク処理

## 入出力対策

| 脅威 | 対策 |
| --- | --- |
| XSS | N/A - HTMLを生成・提供しない |
| SQLインジェクション | N/A - データベースを使用しない |
| CSRF | N/A - Webアプリケーションではない |
| コードインジェクション | `eval`/`load-file`未使用。rewrite-cljによる安全なAST解析のみ |
| パストラバーサル | URI正規化・検証を実施 |
| コマンドインジェクション | コマンド引数をベクトル形式で実行（シェル展開なし） |

### 入力検証

**LSPメッセージ検証**:
- 全リクエスト/レスポンスはclojure.specによるスキーマ検証
- 不正データはログ記録され、処理スキップ

**実装箇所**: `cli/src/clojure_lsp/server.clj` (lines 63-73)

```clojure
(defmacro conform-or-log
  "Provides log function for conformation, while preserving line numbers."
  [spec value]
  (let [fmeta (assoc (meta &form)
                     :file *file*
                     :ns-str (str *ns*))]
    `(jsonrpc.coercer/conform-or-log
       (fn [& args#]
         (log! :error args# ~fmeta))
       ~spec
       ~value)))
```

**URI/パス検証**:
- URI正規化（スキーム、ドライブレター対応）
- URLデコード処理（例外安全）

**実装箇所**: `lib/src/clojure_lsp/shared.clj` (lines 158-187, 199-206)

```clojure
(defn ^:private conform-uri
  [uri {:keys [encode-colons-in-path? upper-case-drive-letter?] :or {upper-case-drive-letter? true}}]
  (let [[match scheme+auth path] (re-matches #"([a-z:]+//.*?)(/.*)" uri)]
    (when-not match
      (logger/error "Found invalid URI:" uri))
    ...))

(defn ^:private unescape-uri [^String uri]
  (try
    (URLDecoder/decode uri (.name StandardCharsets/UTF_8))
    (catch UnsupportedOperationException _ uri)
    (catch IllegalArgumentException _ uri)))
```

**EDN設定ファイル読み込み**:
- リーダーマクロは`re-pattern`のみに制限
- 危険なリーダー（`#'`等）は許可されない

**実装箇所**: `lib/src/clojure_lsp/config.clj` (lines 17-23)

```clojure
(defn read-edn-file [^java.io.File file]
  (try
    (->> (slurp file)
         (edn/read-string {:readers {'re re-pattern}})
         shared/keywordize-first-depth)
    (catch Exception e
      (logger/error "WARNING: error while reading" (.getCanonicalPath file) ...))))
```

### プロセス実行のセキュリティ

クラスパス取得のため外部コマンド（lein, clojure, boot等）を実行する。

**セキュリティ対策**:
- ホワイトリスト戦略: `clojure`, `lein`, `boot`, `npx`, `bb` のみ対応
- 実行可能ファイルの完全パス探索
- コマンド引数をベクトル形式で実行（シェルインジェクション防止）
- 環境変数の統合管理

**実装箇所**: `lib/src/clojure_lsp/classpath.clj` (lines 52-98)

```clojure
(defn ^:private lookup-classpath! [root-path {:keys [classpath-cmd env]}]
  (let [command (string/join " " classpath-cmd)
        env-with-default (merge {} (System/getenv) env)]
    (logger/info logger-tag (format "Finding classpath via `%s`" command))
    (try
      (let [{:keys [exit out err]} (apply shell (into classpath-cmd
                                                      (cond-> [:dir (str root-path)]
                                                        env (conj :env env-with-default))))]
        ...)))
```

## 監査ログ

| ログ種別 | 記録内容 | 保持期間 |
| --- | --- | --- |
| 起動ログ | サーバー起動、クライアント接続情報 | セッション中 |
| 操作ログ | LSPリクエスト/レスポンス（デバッグ時） | セッション中 |
| エラーログ | 例外、警告、解析エラー | セッション中 |
| クラスパスログ | 外部コマンド実行、結果 | セッション中 |

**ログ出力設定**: `cli/src/clojure_lsp/server.clj` (lines 75-86)

```clojure
(defn ^:private enhance-timbre-log-data [log-id db* data]
  (-> data
      (assoc :hostname_ "")  ;; hostname をマスク
      (assoc-in [:context :log-id] log-id)
      (assoc-in [:context :client-name] (-> @db* :client-info :name))
      (assoc-in [:context :client-version] (-> @db* :client-info :version))
      (assoc-in [:context :server-version] (shared/clojure-lsp-version))
      (assoc-in [:context :os-name] (System/getProperty "os.name"))
      (assoc-in [:context :os-version] (System/getProperty "os.version"))
      (assoc-in [:context :os-arch] (System/getProperty "os.arch"))
      (assoc-in [:context :project-root-uri] (:project-root-uri @db*))))
```

## リソース制限

| 項目 | 設定値 | 説明 |
| --- | --- | --- |
| リクエストタイムアウト | 60秒 | 分析待機の最大時間 |
| 診断デバウンス | 25ms | 診断更新の最小間隔 |
| 変更デバウンス | 50ms | ドキュメント変更の最小間隔 |
| ファイル監視デバウンス | 1000ms | ファイルシステム監視の最小間隔 |
| キャッシュ書き込み前GC | 有効 | OOM防止のため手動GC実行 |

**実装箇所**: `cli/src/clojure_lsp/server.clj` (lines 42-44)

```clojure
(def diagnostics-debounce-ms 25)
(def change-debounce-ms 50)
(def watched-files-debounce-ms 1000)
```

## 備考

### セキュリティ上の前提条件

1. **信頼された環境での実行**: Clojure-LSPはローカル開発環境での使用を前提としており、信頼されたエディタ/IDEからの接続のみを想定
2. **ファイルシステム権限**: 実行ユーザーのファイルシステム権限に依存
3. **外部ツール**: lein, clojure等の外部ツールはユーザーがインストール・管理

### 既知の制限事項

1. **認証機構なし**: LSP仕様に認証が定義されていないため、クライアント認証は行わない
2. **ログ情報**: プロジェクトパス、環境変数情報がログに記録される可能性あり
3. **PATHハイジャック**: 外部コマンド実行時にPATH環境変数に依存

### 推奨セキュリティプラクティス

1. 信頼されたLSPクライアント（VS Code, Emacs, IntelliJ等の公式プラグイン）のみを使用
2. プロジェクト設定ファイル（`.lsp/config.edn`）に機密情報を含めない
3. ログファイルの適切な権限設定とローテーション管理
4. 定期的なClojure-LSPのアップデート
