--- name: basercms-security-advisory description: baserCMS のリポジトリセキュリティアドバイザリ(GHSA・triage含む)対応を、一覧取得→指摘検証→課題別の修正→プライベートフォーク/ブランチ/PR作成→ローカル検証まで一気通貫で扱う手順とスクリプト。「セキュリティアドバイザリを確認」「triageの脆弱性を検証」「アドバイザリごとにフォークとPRを作って」「脆弱性修正をプルリクにまとめて」等のときに使う。Copilot/GHAはアドバイザリforkで使えないためローカル検証(/code-review・basercms-unittest)を正とする点、push反映待ちリトライ、共有ファイルのhunk分割、base追従の定番競合解決を収録。 license: MIT --- # baserCMS セキュリティアドバイザリ対応ガイド baserCMS のリポジトリセキュリティアドバイザリ(GHSA・triage 含む)を、一覧→検証→課題別修正→フォーク/ブランチ/PR→ローカル検証まで一気通貫で扱う。スクリプトは `scripts/` 配下。**base ブランチは実行時の現在のブランチ**(事前に対象リリースブランチを checkout しておく)。 ## 0. 前提 - `gh` CLI 認証済み。対象 upstream は `BCSA_UPSTREAM`(既定 `baserproject/basercms`)。 - **Copilot レビューと GitHub Actions はアドバイザリのプライベートフォークでは使えない**。レビューはローカル `/code-review`、テストはローカル Docker(`basercms-unittest`)が正。 - フォーク full_name=`/basercms-<小文字GHSA>`、remote=`sec-<小文字GHSA(先頭ghsa-除去)>`、ブランチ=`security/`。 ## 1. 一覧と分類 `scripts/list-advisories.sh [--state triage]` で state別件数と一覧を取得し、triage を抽出する。 ## 2. 指摘の検証 `scripts/fetch-advisory.sh ` で詳細を取得し、**現在のブランチの実コード**と突き合わせて「的確 / 不正確 / 非該当」を判定する。対象が多い場合は読み取り専用の並列サブエージェントで分担する。フレームワークのデフォルト保護(ORM バインド / slug エンコード / `h()` 出力)で再現しないものは非該当として却下し、具体的な PoC を要求する。 ## 3. 修正方針の確定 同一 sink の重複アドバイザリ、共有ファイルの hunk 分割、非該当の却下を整理する。重複の扱い(複数 fork へ同一修正 / 片方を重複クローズ)など判断が要る点はユーザーに確認する。 ## 4. 課題別フォーク/ブランチ/PR アドバイザリ単位で: 1. `scripts/create-fork-branch.sh ` — フォーク作成 → remote 追加 → 現ブランチ起点でブランチ作成 2. 該当 hunk のみ適用(複数アドバイザリが同一ファイルを触る場合は `git checkout … -- file` で全取りせず hunk 単位で手適用) 3. どの脆弱性をどう直したか明確なメッセージでコミット 4. `scripts/push-with-retry.sh security/` — フォーク反映待ちのリトライ付き push 5. `scripts/open-pr.sh [--title T] [--body-file F]` — base=現ブランチで PR 作成 ## 5. ローカル検証 1. `scripts/build-integration.sh [統合ブランチ名]` — 現ブランチ+全 `security/GHSA-*` をマージ(競合は停止) 2. `scripts/run-tests.sh` — ローカル全テスト(詳細は `basercms-unittest`) 3. 必要なら統合ブランチを個人フォーク(`<個人フォーク名>`)へ push して GHA を回す(**アドバイザリ fork では GHA は動かない**) ## 6. 最新 base への追従 origin/base が進んだら各 PR ブランチへ base をマージし、push し直す。定番競合: - `order() → orderBy()`(CakePHP 5.2 改名) - パス検証 `realpath() === false` バイパス修正 × `$fullPath` 検証 の併合 ## 6b. リリース後の取り込み確認とブランチ整理 アドバイザリ fork の PR を GitHub 上でマージすると、base ブランチには **「Merge commit from fork」という 1 コミット(squash)** として入る。`security/` ブランチ自体は base の祖先にならないため、`git merge-base --is-ancestor` や `git branch --merged` では「未マージ」に見える。取り込み確認は**内容差分**で行う。 1. 対象ブランチが変更したファイル一覧を取り、そのファイルだけを base と比較する(差分ゼロなら取り込み済み): ``` base=$(git merge-base security/ origin/5.4.x) files=$(git diff --name-only $base security/) git diff --name-only origin/5.4.x security/ -- ${=files} # zsh。bash は $files ``` 差分が残るファイルは `git log ..origin/5.4.x -- ` で「取り込み後に別コミットで触られた」だけかを確認する。 2. 取り込み済みと確認できた `security/*` は `git branch -D` で削除する(削除は承認を得てから。10/22 など次回リリース分・顧客向けパッチ(`consolidated-5.2.x`)・未取り込み分は残す)。 3. **同じファイルを触る複数のアドバイザリ**(例 BlogTags API の v9g2 と 636g)は、先にマージした方の fork コミットに後の修正が同梱されていることがある。後の PR をマージしても差分ゼロなら、そのリリースで既に塞がっている。 4. 5.3.x → 5.4.x の系列マージでは VERSION.txt 1 行目・composer.json・composer.lock が必ず競合する。**バージョン値は上位系列(ours)を採用**し、VERSION.txt には下位系列のリリースブロックだけを 5.4.x のブロックの下に追加する。 ## 7. 落とし穴レシピ - **Copilot/GHA 不可**: アドバイザリ fork では使えない。ローカル検証が正。 - **push 反映待ち**: 新規 fork 直後は `remote rejected (failure)`。終了コードでリトライ(`->` 等の文字列で成功誤検知しない)。 - **共有ファイルの hunk 分割**: 例 `PluginsService` の basename と php 実行パス検証は別アドバイザリ。hunk 単位で分けて適用。 - **同一 sink の重複**: 同一修正を複数 fork へ、または片方を重複クローズ。 - **非該当の見極め**: framework デフォルト保護で再現しないものは却下。報告時点のブランチ状態まで遡って確認。 - **フルスイートのフレイキー**: `CreateReleaseCommandTest`(実 composer 実行)は単体では緑。環境要因を切り分ける。 - **認可境界**: `permission.php` の Api/Admin と Admin の `auth` 整合は、管理画面 SPA(ビルド済み JS まで)の依存を確認してから変更。 - **古い系列を後からリリースするとき monorepo-builder が止まる**: `ReleaseGuard` は「ローカルタグのうち committer date が最新のもの」より大きいバージョンしか通さない(系列別の比較は無い)。5.4.0 の後に 5.3.1 を出すなら、リリース作業用クローンで `git tag -d 5.4.0` してから `vendor/bin/monorepo-builder release 5.3.1` を実行し、終わったら `git fetch origin --tags` で戻す。リモートのタグには影響しない。 - **prepare release が未追跡ファイルを巻き込む**: monorepo-builder の release は作業ツリーの未追跡ファイルもコミットする。下位系列(5.3.x)の `.gitignore` に上位系列だけのプラグイン(`webroot/bc_burger_editor`・`webroot/bc_mcp` のシンボリックリンク)が無いと、そのままタグに入る。リリース前に `git status --short` が空であることを確認し、系列ごとの `.gitignore` を揃える。混入したら `git rm --cached` と `.gitignore` 追記で直す。 - **Packagist の反映は Web 表示より遅れる**: split ワークフロー成功後、packagist.org のページに新バージョンが出ていても、Composer が読む `repo.packagist.org/p2//.json` への反映は数分遅れる。`Root composer.json requires ... does not match the constraint` はこの遅れが原因なことが多い。`composer show --all ` で `versions` を確認し、数分待って再実行。キャッシュ削除はホストではなく**アップデートを実行しているコンテナ内**で行う。 - **誤ってタグを出したときの取り下げ**: 本体だけでなく split 先の全リポジトリ(`split_monorepo.yml` の一覧)から `gh api --method DELETE /repos/baserproject//git/refs/tags/` で消す。Packagist は再クロールで「No longer found in upstream」となり自動で消える。5.x ブランチに残ったリリースコミット(VERSION.txt 1 行目・composer.json)は `X.Y.Z-dev` に戻すコミットを別途入れる。 ## 8. 補助スクリプト一覧 | スクリプト | 引数 | 役割 | |---|---|---| | list-advisories.sh | `[--state S]` | アドバイザリ一覧・集計 | | fetch-advisory.sh | `` | 個別詳細取得(/tmp/bc-advisories へ保存) | | create-fork-branch.sh | `` | フォーク作成→remote→現ブランチ起点ブランチ | | push-with-retry.sh | ` [max]` | 反映待ちリトライ push | | open-pr.sh | ` [--title T] [--body-file F]` | base=現ブランチで PR 作成 | | build-integration.sh | `[統合ブランチ名]` | 全 PR を統合ブランチへマージ | | run-tests.sh | `[--filter X]` | ローカル全テスト(basercms-unittest 連携) | ## 9. 既存スキル連携 - **連絡・記録・公開物は本スキルの範囲外**: 報告者への返信コメント、GHSA の受理/深刻度/影響・修正版/credits/重複クローズなどメタ情報の更新、CVE 申請状況、JPCERT/JVN との往復、ベンダステートメント、公式サイトの脆弱性情報ページ原稿、管理表/課題/チャットへの記録、リリース当日チェックは、脆弱性ハンドリング(調整・広報)側の手順で扱う。本スキルは「コードの検証・修正・フォーク/PR・ローカル検証」に集中し、結論(該当/非該当、ブランチ名、PR URL、テスト結果)を返す。 - テスト実行: `basercms-unittest` - 移行起因の競合・非推奨: `cakephp-migration` / `php-migration` / `basercms-plugin-migration`