.knowns/config.jsonファイルにおける、セキュリティが不十分なLSPバイナリパスの設定を介した、OSコマンドインジェクションの脆弱性

## 概要

Knowns Language Server Protocol (LSP)の検出および起動プロセスに、重大な**任意のコード実行(ACE)**の脆弱性が存在します。システムは、プロジェクトレベルの`.knowns/config.json`ファイルで定義されている`settings.lsp.languages.<lang>.binary`フィールドを、何の検証も行わずに信頼しています。

このフィールドは、管理されているバイナリの許可リストに対して**一切検証されません**。また、絶対パスが暗黙的に受け入れられるため、悪意のあるリポジトリ(または、設定が改ざんされた正当なリポジトリ)を開くと、攻撃者が制御するバイナリが即座に実行されます。ペイロードは、セッションごとに**2回**実行されます。1回目は、初期の`runVersionCheck`(ヘルスチェック)時、もう1回は、`Server.Start()`を介してLSPサーバープロセスが起動される際に実行されます。以前に特定された設定の上書きの脆弱性と組み合わせることで、この欠陥は、完全に認証されていないリモートコード実行の脆弱性につながります。

## 影響を受けるファイルパス

| ファイルパス | 役割 | 脆弱性と実行への影響 |
| :--- | :--- | :--- |
| **`internal/models/config.go`** | 検証の欠落 | **バイナリの検証不足(CWE-829):** `ProjectSettings.Validate()`は、タスクのライフサイクルの期間形式を検証しますが、`LSPLanguageSettings.Binary`フィールドは**完全に無視されます**。絶対パス、シェルインタープリター、および信頼できない実行ファイルが、何の警告もなく受け入れられます。 |
| **`internal/lsp/detect.go`** | 実行先 #1 | **サニタイズされていないヘルスチェックの実行:** `Detector.resolve()`は、検証されていないオーバーライドを`exec.LookPath()`に渡し、その後、`runVersionCheck()`を呼び出します。この関数は、攻撃者が制御するバイナリに対して`cmd.Run()`を、何の検証もなく呼び出します。 |
| **`internal/lsp/server.go`** | 実行先 #2 | **サニタイズされていないLSPサーバーの起動:** `Server.Start()`は、解決されたバイナリパスを`knownsprocess.Command()`に渡し、`cmd.Start()`を介して、長期間実行されるバックグラウンドプロセスとして起動します。これにより、ペイロードが2回目の実行されます。 |

## 根本原因

### 設定スキーマにおける検証の欠落
`internal/models/config.go` の `ProjectSettings.Validate()` 関数は、`.knowns/config.json` から読み込まれたプロジェクト設定を検証する役割を担っています。しかし、この関数はタスクのライフサイクル期間のみを検証します。

```go
func (s ProjectSettings) Validate() error {
settings := s.EffectiveTaskLifecycle()
if _, err := ParseTaskLifecycleDuration(settings.ArchiveAfter); err != nil { ... }
// s.LSP.Languages[lang].Binary に対する検証は行われていない
return nil
}
```

`LSPLanguageSettings` 構造体は `Binary` という文字列フィールドを公開しています。悪意のあるプロジェクトが読み込まれた場合、この文字列は変更されずに LSP の解決パイプラインに渡されます。

### LSP検出器におけるブラインド実行
`internal/lsp/detect.go` の `Detector.resolve()` 関数は、設定ファイルから取得した `override` という文字列を受け取り、それを実行パイプラインに強制的に注入します。

```go
func (d *Detector) resolve(ctx context.Context, root string, lang Language, override string) (ServerCommand, bool) {
binaries := lang.Binaries
if override != "" {
binary := Binary{Name: override} // 攻撃者が注入した悪意のあるパスがここに挿入される
binaries = []Binary{binary}
}
for _, binary := range binaries {
path, err := d.LookPath(binary.Name) // 絶対パスを受け入れる (例: /tmp/evil.sh)
// ...
err = d.RunCheck(checkCtx, path, binary.CheckArgs...) // 実行 #1
// ...
}
}
```

`d.RunCheck` は `runVersionCheck` に対応しており、`knownsprocess.CommandContext(ctx, path, args...).Run()` を使用してバイナリを実行します。

### 制約のないプロセス生成
`internal/lsp/server.go` では、LSP マネージャがサーバーを起動する際に、同じように改ざんされたパスが実行されます。

```go
func (s *Server) Start(ctx context.Context) error {
// ...
cmd := knownsprocess.Command(s.Command.Path, s.Command.Args...) // 実行 #2
cmd.Dir = s.Root
// ...
if err := cmd.Start(); err != nil { ... }
}
```

## 攻撃経路

| フェーズ | リクエスト / アクション | 効果 |
| :--- | :--- | :--- |
| **1. 準備** | 攻撃者が、`{"settings":{"lsp":{"languages":{"go":{"binary":"/tmp/evil.sh"}}}}}`という内容を含む`.knowns/config.json`ファイルをリポジトリに登録します。 | プロジェクト内に悪意のある設定が埋め込まれます。 |
| **2. 実行** | 標的となるユーザー(またはAIエージェント)が、リポジトリをクローンし、`knowns mcp`または`knowns browser`で開きます。 | 自動検出機能がプロジェクトをスキャンし、`.go`ファイルを見つけ、悪意のある設定を読み込みます。 |
| **3. 実行 (ヘルスチェック)** | `Detector.resolve()`が`runVersionCheck("/tmp/evil.sh", "version")`を呼び出します。 | **リモートコード実行(RCE)#1**が、バックグラウンドで静かに実行されます。 |
| **4. 実行 (LSP起動)** | `Server.Start()`が、同じバイナリを使用して`cmd.Start()`を呼び出します。 | **RCE実行#2**が発生し、悪意のあるプロセスが長期間実行されるデーモンとして起動されます。 |

**連鎖攻撃ベクトル(認証なしRCE):**
**設定上書き**の脆弱性(`code.replace`によるパストラバーサル)と組み合わせることで、リモートの攻撃者は、標的プロジェクトの`.knowns/config.json`ファイルを上書きし、ペイロードを注入し、LSPの再起動(`docs.update`またはサーバーのリロード経由)をトリガーすることで、**ユーザーの操作なしにリモートコードを実行**することができます。

## 分析

この脆弱性は、**信頼できない制御領域からの機能の組み込み(CWE-829)**の典型的な例です。これは、IDE/エディタの世界では「悪意のあるワークスペース」の脆弱性として知られており、過去のVS Codeにおける`.vscode/settings.json`ファイルが、悪意のあるインタープリタのパスを指すことができた脆弱性と同様です。

根本的な問題は、実行可能ファイルのパスに対する厳格な許可リストがないことです。`exec.LookPath()`に依存しているため、コードは攻撃者が絶対パス(`/abs/path/evil.sh`)またはスラッシュを含むパス(`./evil.sh`)を提供することで、バイナリ名の解決を回避できるようにしています。

さらに、この動作はプロジェクトの読み込み時に**自動的に**実行されます。現代のAIを活用した開発ワークフローでは、AIエージェントがプロジェクトファイル(`.go`、`.ts`、`.py`など)を自動的にスキャンし、コンテキストを提供します。LSP検出器はファイル拡張子に基づいてトリガーされるため、被害者またはエージェントが明示的に「ビルド」または「スタート」コマンドを実行する必要はありません。エージェントがワークスペースをインデックス化するだけで、ペイロードが実行されます。

## 修正

*パッチは現在、[New Release](https://github.com/knowns-dev/knowns/releases)で利用可能です。*

影響を受けるパッケージ:
- npm (JavaScript) knowns: 影響を受けるバージョンは0.29.1以下、0.30.0で修正済み。