refactor(csp): #176 B 案 PR 8 (scope 縮小) — Gs1Databar SVG cleanup + Astro 検出網 + setProperty CSP3 制約記録 ([067]) - #303
Conversation
最終 flip + cleanup の brainstorming 結果を spec として保存。 Gs1Databar SVG `currentColor` 化 + CSP `style-src 'unsafe-inline'` 削除 + `stripMetaStyleSrc` 撤去 + test 群 strict 化 + Astro 検出網 追加 + decisions.md [067] + SoT 更新 を 7 commit で畳む計画。 参照: docs/projects/issue-176-b-plan-progress.md (PR 6 必須チェックリスト)
spec (commit 900eb84) を bite-sized step に分解した plan を保存。 7 task (commit 1〜7) + Pre-Task Setup + Post-Implementation 検証 で構成、subagent 1 体 (sonnet) 直列 dispatch を前提とする。 参照: docs/superpowers/specs/2026-05-08-issue-176-b8-final-flip-design.md
…ntColor 化 dangerouslySetInnerHTML 経由で DOM 注入される SVG <text> の style="fill:var(--color-text)" を fill="currentColor" + 親 div の color: var(--color-text) で表現。CSP style-src strict 化 (B 案 最終 flip 別 PR で実施予定) 後に silent drop されるのを防ぐ事前 refactor。 - src/utils/gs1-databar.ts: fill="#000000" + style="..." 削除、 fill="currentColor" 化 - src/components/tools/Gs1Databar.tsx: 親要素に .gs1-svg-container 付与 - src/styles/global.css: @layer components に .gs1-svg-container 追加
PR 7a / 7b で Astro inline style 65 件全廃後の永続的回帰防止網。
新規 .astro ファイルで style="..." 属性を持ち込むと CSP violation で
silent drop されるため、自動検出網に組み込む。
- src/{components,layouts,pages}/**/*.astro を glob 対象に
- regex `\sstyle\s*=\s*"[^"]*"`: 前置スペース必須で <style> block と区別
- 陽性対照 1 件追加 (悪意ある style 属性が確実に検出されることを assert)
…CSP3 制約発覚 + 延期記録
PR 8 で `style-src 'self'` strict 化を試みた E2E で 11 件 violation
発覚。ResultTable.tsx の el.style.setProperty('--col-width', ...)
(PR 1.5 由来) が CSP3 仕様で style-src の制御対象であり、'unsafe-inline'
/ hash / nonce が必要。連続値ゆえに hash 列挙不可。
決定: ResultTable refactor (Constructable Stylesheets 候補) を PR 9 で
先行、B 案最終 flip は PR 10 へ延期。本 PR は scope 縮小し Gs1Databar
SVG cleanup + Astro 検出網 + 本 entry 記録のみで merge。
Lessons learned (詳細は [067] 本文):
- CSSOM API ≠ CSP style-src 対象外 という PR 1.5 当時の前提は誤り
- migration test の検出 regex は実 violation を保証しない
- ブロッカー検出時は「前提崩壊」と overstating せず解決策を冷静に列挙
進捗 table を更新し、PR 7b を merged 状態 (#299 87d705a) + PR 8 を scope 縮小状態 + PR 9 (ResultTable refactor) / PR 10 (B 案最終 flip) を新規追加。 着手済 PR 履歴セクションに PR 8 (scope 縮小)、PR 9 (新規)、PR 10 (新規) のメモを追記。setProperty CSP3 制約発覚の経緯と post-mortem は decisions.md [067] へ集約。 本 PR (PR 8) は spec / plan の commit 0/-1 + 4 commit (Gs1Databar / Astro 検出網 / decisions [067] / 本 SoT 同期) で構成。元 spec / plan の 7 commit 計画は backup/pr8-full-original branch に保全。
🖼️ Visual Regression Test 結果
|
PR #303 レビュー — 多角評価 (security / frontend / logic / 規約)複数 commit にまたがる変更ですが、実コード差分は実質 3 ファイル / 約 7 行 + テスト 25 行 + ドキュメント大量、という比率です。scope 縮小の判断 + post-mortem の質 が本 PR の主たる価値という認識でレビューしました。 CI: ✅ 高く評価する点1. scope 縮小の意思決定プロセス
2. Gs1Databar SVG の
|
| 項目 | 状況 |
|---|---|
| base = develop | ✅ 明示済 |
本文 --body-file 経由 |
✅ (PR description はバックティック化けなし) |
| 言語 = 日本語 | ✅ |
| aria-* 削除 | ✅ なし (Gs1Databar.tsx の aria-label / aria-live は維持) |
| pre-create scope 確認 | ✅ 8 ファイル全て develop diff 内 |
まとめ
- security: CSP 観点で正しい方向の refactor。
dangerouslySetInnerHTML経路は escape 済で widen なし。 - frontend / UX:
currentColor採用は標準パターン、VRT pass で視覚的同一性担保。 - logic: refactor は意図通り、テストの陽性対照も付加。Astro regex のシングルクォート未対応は 割り切り として許容範囲。
- post-mortem の質: [067] の問題分析・解の評価表・lessons learned は将来の reviewer / 別 PC 作業者にとって reference value が高い。
- PR 規約: 4 点 (base / body-file / 日本語 / aria) いずれも遵守。
approve 推奨。M-1 〜 M-3 は 任意取り込み、merge 前提の指摘ではない。merge 後 PR 9 着手前に I-1 (CSP3 spec の最新動向) は再確認推奨。
PR #303 self-review (approve 寄り、minor 指摘 3 件任意取り込み) のうち 3 件すべてを反映。 M-1: Astro detector regex の検出範囲拡張 - inline-style-migration.test.ts: regex を ダブルクォート / シングルクォート / {expression} の 3 形式対応に拡張 (`/\sstyle\s*=\s*("[^"]*"|'[^']*'|\{[^}]*\})/`) - 陽性対照 2 件追加 (シングルクォート / expression) - 既存陽性対照 1 件のテスト名を「ダブルクォート」と明記 - 効果: 将来 frontmatter 由来の動的注入や JSX 風 expression 形式の inline style が混入した場合の silent skip を防ぐ M-2: Gs1Databar SVG `currentColor` 採用根拠コメント追加 - src/utils/gs1-databar.ts: `<text>` 生成箇所付近に親要素 `.gs1-svg-container` ({ color: var(--color-text) }) との依存関係を明記 - 効果: 将来 Gs1Databar.tsx の親要素 className を変更した瞬間に SVG text デフォルト色 (UA 依存、通常 black) にフォールバックする事故を防ぐ M-3: plan ファイル冒頭に scope 縮小 banner 追加 - docs/superpowers/plans/2026-05-08-issue-176-b8-final-flip.md: 1114 行の 当初 7-commit 計画に対し、本 PR で実施したのは Task 1/5/6/7 のみで Task 2/3/4 は PR 9/10 へ延期した経緯を冒頭で明示 - 効果: reviewer / 将来読者が plan と実装を crossref した際に 「Task 2-4 未実装」に見える混乱を解消、[067] / #304 / #305 への導線を確立
|
レビューありがとうございます。M-1 / M-2 / M-3 の 3 件すべて取り込みました (commit 6d14035)。 対応内容✅ M-1: Astro detector regex の検出範囲拡張
✅ M-2: SVG
|
再レビュー —
|
| 入力 | マッチ | 妥当性 |
|---|---|---|
<div style="color: red" /> |
✅ | double 検出 |
<div style='color: red' /> |
✅ | single 検出 |
<div style={cssExpr} /> |
✅ | expression 検出 |
<div style={{color: 'red'}} /> (JSX) |
✅ | nested は \{[^}]*\} で外側まで部分マッチ → toMatch 真 |
<style>...</style> |
❌ | 前置スペースなし、対象外 |
意図通り。
✅ M-2: gs1-databar.ts 採用根拠コメント
src/utils/gs1-databar.ts:226-232 の 7 行コメントは「何を / どこに依存 / 壊れたらどうフォールバック / なぜそうしたか」の 4 点を網羅:
fill="currentColor"が親要素のcolorを継承する仕組み- 親要素は
Gs1Databar.tsxのdangerouslySetInnerHTMLラッパ<div>で.gs1-svg-containerクラス経由 - 維持すべき規約 (className 変更時は同等の
color設定を残す) - フォールバック挙動 (UA デフォルト = 通常 black)
- 参照 [067] /
#176B 案
将来 Gs1Databar.tsx を他者が触る時、grep で gs1-svg-container を辿るだけで意図が拾える設計。
✅ M-3: plan banner 追加 + 想定外の補強
docs/superpowers/plans/2026-05-08-issue-176-b8-final-flip.md:3 の banner は私の提案を超えて以下まで含める:
- Task 2/3/4 が rebase 除外された経緯 + [067] 参照
- 後続 PR の issue 番号確定値 (
#304/#305) — 確認したところ両 issue とも実在 / OPEN: - backup branch
backup/pr8-full-originalの存在と push しない方針
これにより plan ↔ 実 issue ↔ [067] の 3 方向 crossref が完成。私の提案は 1 文だったが、実装はその 3 倍の情報密度になっており、archival 価値が大きく増している。
状態確認
| 項目 | 状態 |
|---|---|
test CI |
✅ SUCCESS |
e2e CI |
✅ SUCCESS |
visual-regression CI |
✅ SUCCESS |
| mergeable | CLEAN |
| base | develop (規約遵守) |
| follow-up issue | #304 / #305 起票済 (OPEN) |
CI required check は全 green、human review approve まで揃えば merge 可能な状態。残作業 (#304 → #305) も独立 issue 化済で本 PR が release blocker になる経路はなし。
結論
- M-1 / M-2 / M-3 はいずれも私の提案範囲を満たしつつ、想定以上の補強あり。
- 新規指摘なし。
- approve 寄りの判断は変わらず。merge は self-review approve + human review の方針に従って判断してください。
[067] post-mortem の質と #304 / #305 への引継ぎが整っているため、本 PR は B 案シリーズの valuable failure case のリファレンス としても機能します。
…リンク反映 (#306) PR #303 (merged e2efd24) の hash placeholder 置換と、PR 9 / PR 10 セクションへの follow-up issue リンク (#304 / #305) 追加。 進捗 table: - PR 8 行: 🔄 PR open → ✅ merged + #303 link + e2efd24 hash - PR 9 行: PR 列を `-` → issue #304 link - PR 10 行: PR 列を `-` → issue #305 link 着手済 PR 履歴セクション: - PR 8: (#TBD) → (#303) - PR 9: (#TBD) → issue #304 link - PR 10: (#TBD) → issue #305 link prettier 整形により table 列幅が再 align (substantive な変更は上記のみ)。
…tructable Stylesheets に refactor (#304) (#307) * docs(spec): #176 B 案 PR 9 設計書 — ResultTable / ToggleGroup の setProperty を Constructable Stylesheets に refactor [067] post-mortem を起点に PR 9 (issue #304) の設計書を起草。 主要な設計判断: - 採用技術: Constructable Stylesheets ([067] 最有力案)、(b) CSS class swap は Phase 0 NG 時の fallback - scope 拡張: ResultTable に加え ToggleGroup も refactor (config-converter violation の真の原因 = ToggleGroup と確認、12 ツールで使用、PR 10 strict 化で全違反するため同 PR 必須) - API 互換: 公開 props (width / minWidth / options) は維持 - 共通 hook: useDynamicStyleSheet (SSR-safe / useId ベース) で Constructable Stylesheet 経路を集約 - defense in depth: assertCssLength で CSS injection 防御 Phase 構成: - Phase 0: minimal repro spec (陽性 + 陰性対照、永続) で Chromium 動作実機確認 - Phase 1: hook + ResultTable + ToggleGroup refactor - Phase 2: 12 ツール spec を strict CSP で local 実行 (run-only / commit せず) - inline-style-migration.test.ts の setProperty 除外を陽性 guard に反転 infra (helpers) と feature の bundle を許容 (PR 5b と同 judgement、helper 30 行規模 + Phase 0 検証は refactor の前提)。 * docs(plan): #176 B 案 PR 9 実装計画 — ResultTable / ToggleGroup CSP refactor spec (`docs/superpowers/specs/2026-05-08-issue-176-b9-...`) に対応する task-by-task の実装計画。10 task / 4 commit (C1-C4) + Phase 2 run-only verification + PR 作成。 各 task は (i) 失敗テスト → (ii) 実装 → (iii) PASS 確認 → (iv) commit の TDD/checkpoint 形式で記述。すべての code block は完全形 (placeholder なし)、コマンドは exact、commit message は HEREDOC 形式の雛形を含む。 * test(e2e): #304 Phase 0 — applyStrictStyleSrcCsp + Constructable Stylesheets 検証 spec PR 9 verification 用の infra を先行投入する。 - helpers.ts: applyProductionCsp の中身を applyCspOverride に切り出し、 applyStrictStyleSrcCsp (style-src 'self' 強制) を追加。PR 10 で PRODUCTION_CSP 自体が strict 化したら本 helper は削除候補。 - csp-constructable-stylesheet.spec.ts (永続): 陽性対照 (インライン <style> 要素は violation 起こす) + 陰性対照 (Constructable Stylesheet は起こさない / CSS 変数経由でも起こさない) の 3 test。PR 10 後も継続 regression 検出網。 陽性対照の probe: el.style.setProperty() を page.evaluate() 経由で呼ぶと CDP Runtime.evaluate は CSP inline style 制約を受けないことを実機確認。 <style> 要素挿入を代替 probe とした (uuid-v7/config-converter の 陽性対照パターン — <script src external> — と同じ CDP bypass 回避方針)。 memory feedback_positive_control_for_gates.md / feedback_infra_feature_separation.md の例外条項 (PR 5b と同 judgement、helper 30 行規模 + Phase 0 検証は refactor の前提) 準拠。 * test(e2e): #304 Phase 0 review fix — STRICT_STYLE_SRC_CSP の drift 防止 + 陽性/陰性対照に前提ヘッダ assert PR 9 Task 1 の code review で指摘された 2 件の Important 修正: - helpers.ts: STRICT_STYLE_SRC_CSP を PRODUCTION_CSP からの replace 派生に変更 し、module load 時の sanity check で drift を CI 検知可能に。PR 10 で PRODUCTION_CSP 側 style-src が strict 化されたら replace は no-op となり 本 helper を削除すべき状態になる - csp-constructable-stylesheet.spec.ts: 3 test 全てで page.goto の Response ヘッダに対し strict CSP が乗っていることを事前 assert。route 注入が静かに 失敗してテストが意味なく pass / fail するのを防ぐ (uuid-v7 / config-converter の既存陽性対照と同パターン) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(ResultTable,ToggleGroup): #304 setProperty を Constructable Stylesheets に refactor PR 8 ([#303]) の親直接 E2E で発覚した CSP3 style-src 'self' 非互換の setProperty 経路を Constructable Stylesheets ([067] (a) 案) に置換。 PR 10 ([#305]) の B 案最終 flip を unblock する。 新規: - src/hooks/useDynamicStyleSheet.ts: SSR-safe (useId ベース) な共通 hook、 `new CSSStyleSheet()` + `document.adoptedStyleSheets` を React lifecycle に integrate - src/utils/css-length.ts: assertCssLength validator、replaceSync 経由の CSS injection 防御 (defense in depth) - src/test-setup.ts: vitest 用 jsdom polyfill (CSSStyleSheet.replaceSync / document.adoptedStyleSheets)。useDynamicStyleSheet を使うコンポーネントを render する全 jsdom test で必要 修正: - src/components/ui/ResultTable.tsx: el.style.setProperty('--result-table-min-width' / '--col-width', ...) 2 件を撤去、useDynamicStyleSheet で per-instance scoped rule を注入。`.result-table-col` class 付与も不要となり撤去 - src/components/ui/ToggleGroup.tsx: useEffect + gridRef + setProperty('--toggle-cols') を撤去、useDynamicStyleSheet で per-instance scoped rule に変更。 layout='wrap' 経路は sheet 生成 skip - src/styles/global.css: `.result-table { min-width: var(...) }` / `.result-table-col { width: var(...) }` 撤去 (Constructable Stylesheet 一本化)。`.toggle-grid` の --toggle-cols, 2 fallback は wrap 経路の silent guard として保持 - vitest.config.ts: setupFiles に test-setup.ts を追加 API 互換: ResultTable の width / minWidth、ToggleGroup の options は不変。 * test(migration): #304 setProperty を陽性 guard に反転 PR 9 で codebase から `el.style.setProperty(...)` が完全消滅したため、 migration test 内で setProperty を意図的に除外していた filter を撤去し、 新規再導入を陽性検出する guard に反転する。 変更: - JSDoc に [067] / PR 9 の経緯を追記、useDynamicStyleSheet hook で代替済の旨明記 - TSX violation 検出を `\.style\.X =` 単独から `\.style\.X =` + `\.style\.setProperty\(` の両方を陽性検出する形に拡張 - 「setProperty は許容パターンとしてスルーされる」test を削除し、 「setProperty(...) は違反として検出される」陽性対照 test に置換 - 既存 「style.X = は陽性検出される」test の filter チェーンも簡素化 参照: docs/decisions.md [067]、PR 9 spec § 5.5 * docs: #304 PR 9 outcome を [067] に追補 + SoT 同期 + PR 10 申し送り記録 - decisions.md [067]: PR 9 outcome section 追加 (採用 = Constructable Stylesheets / scope 拡張 = ToggleGroup 同梱 / 実装サマリ / Phase 2 で 発覚した Astro island runtime style hash 取り込み課題を PR 10 申し送り) - issue-176-b-plan-progress.md: PR 9 行を「実装中 + scope 拡張」に更新、 詳細 section に ToggleGroup 同梱 / Phase 0 永続検証 / migration test 反転 / subagent 委譲方針 / PR 10 申し送り事項を追記 Phase 2 strict CSP local verification で 13 ツール spec が Astro 島ランタイム の固定 inline style 由来の violation で fail することを確認。PR 9 の React refactor 自体は無問題 (Phase 0 spec が `/` で pass)、本件は [064] / stripMetaStyleSrc 構造由来で PR 10 責務範囲のため scope を訂正し、PR 10 で 解決する旨を decisions.md / SoT 両方に明記。 merge 後の hash 反映は別 chore PR で対応 (PR 6 / 7a / 7b / 8 と同パターン)。 --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
概要
#176B 案 PR 8 — scope 縮小版。当初はstyle-src 'unsafe-inline'削除の最終 flip を予定していたが、実装後の親直接 E2E でResultTableのel.style.setProperty('--col-width', ...)(PR 1.5 由来) が CSP3 仕様でstyle-srcの制御対象であることが判明し、strict 化で 11 件 violation。連続値ゆえ hash 列挙不可のため、ResultTable refactor を別 PR に分離して延期 することにした。詳細経緯と対応方針は
docs/decisions.md [067]を参照。主な変更 (4 commit)
currentColor化 (commit 1/4):dangerouslySetInnerHTML経由で DOM 注入される SVG<text>のstyle="fill:var(--color-text)"をfill="currentColor"+ 親.gs1-svg-container { color: var(--color-text) }に置換。将来の strict 化時に silent drop されるのを防ぐ事前 refactor。inline-style-migration.test.tsにsrc/{components,layouts,pages}/**/*.astroglob を並列追加 + 陽性対照 1 件。新規.astroファイルでstyle="..."属性を持ち込むと検出。docs/decisions.md [067]追加 (commit 3/4): ResultTablesetPropertyの CSP3 制約発覚 + B 案最終 flip を別 PR に延期する design decision を記録。3 つの解 (Constructable Stylesheets / CSS class swap /'unsafe-hashes') の評価と採用方針 (PR 9 で Constructable Stylesheets 検証 → PR 10 で B 案最終 flip) を含む。docs/projects/issue-176-b-plan-progress.mdを更新し PR 7b ✅ merged + PR 8 (scope 縮小) + 新規 PR 9 (ResultTable refactor) / PR 10 (B 案最終 flip) を追加。当初計画から削除 (rebase で除外)
backup/pr8-full-originalbranch で保全 (push しないので消える可能性に注意):_headers+csp.tsのstyle-src 'unsafe-inline'削除 → PR 10 へ延期astro.config.mjsからstripMetaStyleSrc()integration 撤去 → PR 10 へ延期headers.test.ts/meta-csp.test.ts/astro-config-csp.test.ts群の strict 化 → PR 10 へ延期関連 PR / issue
#176B 案進捗 (PR 1〜7b 完了 / PR 8 部分達成):setProperty導入元withProductionCspラッパ)検証
npm run test全 pass (780 tests)npm run build成功 (18 pages)npm run test:e2e全 pass (146 passed / 1 skipped、style-src 'unsafe-inline'維持のため violation 不発)npx astro checkエラーなしnpx prettier --check .cleangrep -c "style={{" src/= 0grep -rEn 'style="[^"]*"' src/components/ src/layouts/ src/pages/= 0post-mortem (要約、詳細は [067])
setProperty) は CSPstyle-src観点で別経路」が誤り。CSP3 仕様 (https://www.w3.org/TR/CSP3/#directive-style-src) ではstyle-srcの制御対象に JavaScript 経由の style modification も含まれる。inline-style-migration.test.tsは文字列 regex で違反を検出するが、これは CSP の実評価とは独立。E2EapplyProductionCspgate を導入していた spec は 11 件あるが、いずれも strict 化 前 の状態で書かれており、strict 化を試みて初めて violation が顕在化した。後続作業 (本 PR merge 後)
setPropertyの Constructable Stylesheets 化検証 + 実装 (PR 9)backup/pr8-full-originalbranch を保全 (PR 10 の reference として使用するか後で判断)🤖 Generated with Claude Code