Quasar Framework: アイコン Genie プロファイルを利用したディレクトリトラバーサル/任意のファイル書き込みの脆弱性

## 脆弱性の詳細

**ファイル**: `icongenie/lib/utils/get-assets-files.js` (35行目、`absoluteName: join(appDir, asset.folder, asset.name)`)
**検証の欠落**: `icongenie/lib/utils/validate-profile-object.js` (`assetsSchema`) — `folder`/`name` は `Joi.string().required().min(1)` でのみ検証され、`..` シーケンスや絶対パスに対する制限はない。
**エントリポイント**: `icongenie/lib/runner/generate.js` (`generate(argv)`) — `profile.assets = userProfile.assets` は、`--profile <file>` オプションで指定されたユーザーが提供した JSON ファイルからそのまま読み込まれる。

### 根本原因
`icongenie generate --profile <file>` コマンドは、生成するアイコン/スプラッシュスクリーンのアセットを記述した JSON "プロファイル" を読み込みます。各アセットのエントリには、生成されたファイルが Quasar プロジェクトディレクトリ (`appDir`) に対して相対的にどこに書き込まれるかを記述する `folder`/`name` が含まれています。`getAssetsFiles()` 関数は、書き込み先を `join(appDir, asset.folder, asset.name)` で構築します。Node.js の `path.join` は、`..` セグメントを数値的に処理し、結果を `appDir` 内に制限しません。この前の検証 (`validateProfileObject` → Joi `assetsSchema`) では、`folder`/`name` が空でない文字列であることのみがチェックされ、`..` の拒否や `appDir` に対する包含チェックはありません。

攻撃者が `folder: "../../../../../../tmp/pwned-by-icongenie"` のように設定したプロファイルは、検証を通過し、ジェネレーターは `writeFile`/`sharp().toFile()` の直接呼び出しによって、攻撃者が制御するアイコン/スプラッシュスクリーンのコンテンツをそのパスに書き込みます。

### 攻撃シナリオ
1. 攻撃者が、「完成済みのIcon Genieプロファイル」(gist、スターターキットのリポジトリ、サポートフォーラムへの添付ファイルなど)を公開します。これは、通常のアイコン生成設定のように見えますが、トラバーサル機能を持つ「folder」というアセットエントリが含まれています。
2. Quasarプロジェクトで開発者が、`icongenie generate --profile malicious-profile.json`(通常の、ドキュメントに記載されているワークフロー)を実行します。
3. `getAssetsFiles()`関数が、プロジェクトディレクトリ外の書き込み先を解決します。これにより、ジェネレーターが攻撃者が制御するコンテンツを、そのパスに書き込みます。例えば、シェル起動ファイル、cronエントリ、またはCI/ビルドスクリプトを書き込んだり、上書きしたりします。

### 影響
- **種類**: CWE-22 パス・トラバーサル / 任意のファイル書き込み
- **認証の必要性**: ネットワーク認証は不要。ローカルCLIの信頼関係が必要。開発者が、自身が完全に作成/監査していないプロファイルに対してicongenieを実行する必要がある。
- **結果**: 実行ユーザーが書き込み可能な任意のパスに、任意のファイルを書き込んだり、上書きしたりできる。書き込み可能なパスは、`..`セグメントの数によって制限される。これにより、永続化(cron/シェルrcファイル)や、サプライチェーン型のコード実行(書き込まれたファイルが後で実行/参照される場合)につながる可能性がある。

### 脆弱なコード (`icongenie/lib/utils/get-assets-files.js`)
```js
export function getAssetsFiles(assets) {
...
return list.map(({ tag, ...asset }) => {
const file = {
...asset,
relativeName: join(asset.folder, asset.name),
absoluteName: join(appDir, asset.folder, asset.name) // コンテナチェックなし
}
...
})
}
```

### 推奨される修正
```js
import { resolve, sep } from 'node:path'

const absoluteName = resolve(appDir, asset.folder, asset.name)

if (absoluteName !== appDir && !absoluteName.startsWith(appDir + sep)) {
fatal(`プロファイルの資産がプロジェクトフォルダから逸脱しています: "${asset.folder}/${asset.name}"`)
}
```

### 検証
v2.21.1で、実際の、改変されていない`icongenie generate()`関数(`icongenie/lib/runner/generate.js`から)を、意図的に脆弱な`malicious-profile.json`ファイルを含むQuasarプロジェクトに対して実行し、エンドツーエンドで検証しました。この`malicious-profile.json`ファイルには、`"folder": "../../outside-target-marker"`という記述が含まれていました。icongenie自身のコンソール出力が、ディレクトリのトラバーサル(`Generated svg: ../../outside-target-marker/pwned-outside-project.svg`)を報告し、生成されたSVGファイルが、プロジェクトフォルダから2階層外のディレクトリに存在することが、独立して検証されました。

上記のような最小限の修正を含む修正ブランチ(`fix/icongenie-path-traversal-asset-folder`)が準備されています。修正されたコードに対して、同じ脆弱なプロファイルを再度実行すると、すぐに`Profile asset escapes the project folder: "../../outside-target-marker/pwned-outside-project.svg"`というエラーメッセージが表示され、プロジェクトフォルダ外に何も書き込まれません。一方、正常なプロファイル(`folder: "public/icons"`)は、以前と全く同じように動作します。

影響を受けるパッケージ:
- npm (JavaScript) @quasar/icongenie: 影響を受けるバージョンは6.1.1未満、6.1.1で修正済み。