Skip to content

fix: XSS脆弱性の修正#6635

Open
kurozumi wants to merge 2 commits intoEC-CUBE:4.3from
kurozumi:audit/security-code-review
Open

fix: XSS脆弱性の修正#6635
kurozumi wants to merge 2 commits intoEC-CUBE:4.3from
kurozumi:audit/security-code-review

Conversation

@kurozumi
Copy link
Contributor

@kurozumi kurozumi commented Feb 24, 2026

Summary

  • テンプレートのHTMLコンテンツに |purify フィルターを追加してXSSを防止
  • JSON出力に JSON_HEX_TAG 等のセキュリティオプションを追加
  • 安全なJSONエンコード用の json_encode_safe フィルターを追加

修正内容

1. テンプレートの修正(|raw|raw|purify

ファイル 対象フィールド
default/Product/detail.twig Product.description_detail
default/Product/list.twig Product.description_list
default/Block/news.twig News.description
default/Help/tradelaw.twig tradelaw.description
default/Shopping/index.twig activeTradeLaw.description
default/Shopping/confirm.twig activeTradeLaw.description

2. JSON出力の修正

ファイル 修正内容
EccubeExtension.php getClassCategoriesAsJson()JSON_HEX_* オプション追加、json_encode_safe フィルター追加
FileController.php 3箇所の json_encode()JSON_HEX_* オプション追加
EditController.php shippingDeliveryTimesjson_encode() に変更
ShippingController.php shippingDeliveryTimesjson_encode() に変更
OwnerStoreController.php requires をコントローラー側でJSON化

Test plan

  • EccubeExtensionTest: 5テスト成功
  • FileControllerTest: 42テスト成功
  • EditControllerTest: 18テスト成功

Related Issue

Fixes #6634

🤖 Generated with Claude Code

Summary by CodeRabbit

  • セキュリティ改善
    • JSON 出力時の HTML エスケープ処理を強化し、管理画面や注文表示などでの埋め込み出力を安全化しました。
    • テンプレートで表示するHTMLをサニタイズする処理を追加/適用し、説明文や法定表示などの出力をより安全にしました。
  • 新機能
    • テンプレートから使える安全な JSON エンコードフィルターを追加しました。

- テンプレートのHTMLコンテンツに|purifyフィルターを追加
- JSON出力にJSON_HEX_TAG等のセキュリティオプションを追加
- 安全なJSONエンコード用のjson_encode_safeフィルターを追加

修正対象:
- Product.description_detail, description_list
- News.description
- tradelaw.description, activeTradeLaw.description
- class_categories_as_json()
- FileController, EditController, ShippingController, OwnerStoreController

Refs EC-CUBE#6634

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 822fafa and fdad301.

📒 Files selected for processing (2)
  • src/Eccube/Controller/Admin/Store/OwnerStoreController.php
  • src/Eccube/Resource/template/admin/Store/plugin_confirm.twig
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Eccube/Controller/Admin/Store/OwnerStoreController.php

📝 Walkthrough

Walkthrough

複数のコントローラーとテンプレートでXSS対策を実施。JSON出力にHTML特殊文字エスケープ(JSON_HEX_*)を追加し、テンプレートの生HTML出力にpurifyフィルターを挿入、さらにTwigに安全なjson_encodeフィルタを追加しました。

Changes

Cohort / File(s) Summary
JSON エンコーディングのセキュリティ強化 (コントローラー)
src/Eccube/Controller/Admin/Content/FileController.php, src/Eccube/Controller/Admin/Order/EditController.php, src/Eccube/Controller/Admin/Order/ShippingController.php, src/Eccube/Controller/Admin/Store/OwnerStoreController.php
JSON出力を `json_encode(..., JSON_HEX_TAG
テンプレートの HTML サニタイズ (フロント)
src/Eccube/Resource/template/default/Product/detail.twig, src/Eccube/Resource/template/default/Product/list.twig, src/Eccube/Resource/template/default/Block/news.twig, src/Eccube/Resource/template/default/Help/tradelaw.twig, src/Eccube/Resource/template/default/Shopping/confirm.twig, src/Eccube/Resource/template/default/Shopping/index.twig
`
管理画面テンプレートの更新
src/Eccube/Resource/template/admin/Store/plugin_confirm.twig
requires のテンプレート埋め込みを json_encode_safe(セーフなjsonエンコード)へ変更。
Twig フィルター拡張
src/Eccube/Twig/Extension/EccubeExtension.php
新しい Twig フィルタ json_encode_safe を追加し、getClassCategoriesAsJson などで JSON_HEX_* フラグを使用するよう修正。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • ji-eunsoo
  • ttokoro20240902

Poem

🐰 小さなウサギの祝辞

コードに跳ねて 森を渡り
危うきスクリプトは もう入れず
JSONもHTMLも やさしく包み
みんなで安心の草原へ 🥕✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PRタイトル「fix: XSS脆弱性の修正」は、変更セットの主要な目的である複数のテンプレートとコントローラーのXSS脆弱性修正を明確に表しており、簡潔で具体的です。
Linked Issues check ✅ Passed PR#6635の変更は、Issue#6634で指定されたすべての主要要件を満たしています。ストアドXSS対策として6つのテンプレートに|purifyフィルターを追加し、JSON出力のXSS対策として複数のファイルのjson_encode()にJSON_HEX_*オプションを追加し、新しいjson_encode_safeフィルターも実装されています。
Out of Scope Changes check ✅ Passed すべての変更がIssue#6634で指定されたXSS脆弱性修正の範囲内です。テンプレートのHTMLサニタイズ、JSON出力のセキュリティオプション追加、新しいTwigフィルターの実装など、セキュリティ修正に直接関連する変更のみが含まれています。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Eccube/Controller/Admin/Store/OwnerStoreController.php (1)

246-256: ⚠️ Potential issue | 🟠 Major

requires をJSON文字列に変更すると plugin_confirm.twig のTwigiテレーション部分が壊れます

コントローラーが requires をJSON文字列として渡すようになったことで、plugin_confirm.twig には以下の問題が生じています:

1. requires|length が配列長ではなく文字列長を返す(Line 213)
Twigの length フィルターは文字列に対して mb_strlen() を呼ぶため、"[]" の長さは 2 です。2 > 0 が常に真となり、依存プラグインがない場合でも「必要プラグイン」カードが表示されてしまいます。

2. {% for plugin in requires %} が空ループになる(Line 219)
Twigの twig_ensure_traversable() は文字列を空配列 [] として扱うため、ループが一切実行されず依存プラグインの一覧が表示されません。

推奨修正:コントローラー側で PHP 配列のまま渡し、テンプレート側で本PRで追加された json_encode_safe フィルターを使用してください。

🔧 doConfirm の修正案
  return [
      'item' => $item,
-     'requires' => json_encode($requires, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT),
+     'requires' => $requires,
      'is_update' => false,
  ];
🔧 doUpdateConfirm の修正案
  return [
      'item' => $item,
-     'requires' => json_encode([], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT),
+     'requires' => [],
      'is_update' => true,
      'Plugin' => $Plugin,
  ];

そして plugin_confirm.twig のLine 119 を以下のように変更してください:

- var requires = {{ requires|raw }};
+ var requires = {{ requires|json_encode_safe }};

Also applies to: 531-547

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Eccube/Controller/Admin/Store/OwnerStoreController.php` around lines 246
- 256, The controller changed 'requires' to a JSON string which breaks Twig
iteration/length checks; revert the change in the doConfirm and doUpdateConfirm
handlers so they pass 'requires' as a PHP array (not json_encode'd) and ensure
templates use the new json_encode_safe filter when a JSON string is needed
(e.g., plugin_confirm.twig uses requires|json_encode_safe for inline JS) —
update the return payloads in the doConfirm and doUpdateConfirm methods to
return the raw $requires array and remove json_encode there so Twig's length and
for-loop behavior work correctly.
🧹 Nitpick comments (1)
src/Eccube/Resource/template/default/Shopping/index.twig (1)

397-397: |raw フィルターは冗長です。|purify のみで十分です

exercise/htmlpurifier-bundle 5.0 の purify フィルターは is_safe => ['html'] で登録されており、HTMLサニタイズ後の出力をTwigに対して安全なHTMLとして宣言します。Twigの raw フィルターは最後に適用される場合にのみ自動エスケープを無効化するため、後続の purify フィルターが存在する場合は機能しません。

{{ activeTradeLaw.description|purify }} に変更し、|raw を削除することで、より明確で冗長性のないコードになります。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Eccube/Resource/template/default/Shopping/index.twig` at line 397, Remove
the redundant |raw filter and leave only the purify filter on the
activeTradeLaw.description output: replace the expression that applies both
|purify and |raw so it uses only {{ activeTradeLaw.description|purify }},
ensuring sanitization is applied and Twig sees the result as safe without the
unnecessary |raw call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Eccube/Controller/Admin/Store/OwnerStoreController.php`:
- Line 248: OwnerStoreController currently writes 'requires' =>
json_encode($requires, ... ) without handling json_encode() returning false;
update the code around the json_encode call in OwnerStoreController so you
either (a) use json_encode($requires,
JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_THROW_ON_ERROR)
inside a try/catch and log/fallback on exception, or (b) capture the result into
a variable (e.g. $requiresJson = json_encode(...);) and if $requiresJson ===
false set $requiresJson = '[]' (or safe JSON string) before assigning to the
template, ensuring the template always receives valid JSON for the requires
variable.

In `@src/Eccube/Resource/template/admin/Store/plugin_confirm.twig`:
- Around line 119-122: The Twig template treats requires as a PHP string
(pre-encoded JSON) which breaks the length check and for-loop; update
plugin_confirm.twig to decode the JSON before Twig-side operations by applying
Twig's JSON decode (e.g., use requires|json_decode or equivalent) wherever you
do requires|length and {% for plugin in requires %} so Twig sees an array, or
alternatively revert OwnerStoreController.php to pass a PHP array instead of a
JSON string and keep the current Twig iteration; ensure all references to
requires in the template (the length check and the for-loop that renders
dependency cards) reference the decoded/array form.

---

Outside diff comments:
In `@src/Eccube/Controller/Admin/Store/OwnerStoreController.php`:
- Around line 246-256: The controller changed 'requires' to a JSON string which
breaks Twig iteration/length checks; revert the change in the doConfirm and
doUpdateConfirm handlers so they pass 'requires' as a PHP array (not
json_encode'd) and ensure templates use the new json_encode_safe filter when a
JSON string is needed (e.g., plugin_confirm.twig uses requires|json_encode_safe
for inline JS) — update the return payloads in the doConfirm and doUpdateConfirm
methods to return the raw $requires array and remove json_encode there so Twig's
length and for-loop behavior work correctly.

---

Nitpick comments:
In `@src/Eccube/Resource/template/default/Shopping/index.twig`:
- Line 397: Remove the redundant |raw filter and leave only the purify filter on
the activeTradeLaw.description output: replace the expression that applies both
|purify and |raw so it uses only {{ activeTradeLaw.description|purify }},
ensuring sanitization is applied and Twig sees the result as safe without the
unnecessary |raw call.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e99202 and 822fafa.

📒 Files selected for processing (12)
  • src/Eccube/Controller/Admin/Content/FileController.php
  • src/Eccube/Controller/Admin/Order/EditController.php
  • src/Eccube/Controller/Admin/Order/ShippingController.php
  • src/Eccube/Controller/Admin/Store/OwnerStoreController.php
  • src/Eccube/Resource/template/admin/Store/plugin_confirm.twig
  • src/Eccube/Resource/template/default/Block/news.twig
  • src/Eccube/Resource/template/default/Help/tradelaw.twig
  • src/Eccube/Resource/template/default/Product/detail.twig
  • src/Eccube/Resource/template/default/Product/list.twig
  • src/Eccube/Resource/template/default/Shopping/confirm.twig
  • src/Eccube/Resource/template/default/Shopping/index.twig
  • src/Eccube/Twig/Extension/EccubeExtension.php

return [
'item' => $item,
'requires' => $requires,
'requires' => json_encode($requires, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

json_encode の失敗を考慮していません(マイナー)

json_encode() は不正な UTF-8 などで false を返す可能性があります。その場合、テンプレートで var requires = false; と出力され、JavaScriptの .map() 呼び出しで TypeError が発生します。上記のコントローラー修正を採用する場合は不要ですが、現行コードを保持する場合は false チェックを追加することを検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Eccube/Controller/Admin/Store/OwnerStoreController.php` at line 248,
OwnerStoreController currently writes 'requires' => json_encode($requires, ... )
without handling json_encode() returning false; update the code around the
json_encode call in OwnerStoreController so you either (a) use
json_encode($requires,
JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_THROW_ON_ERROR)
inside a try/catch and log/fallback on exception, or (b) capture the result into
a variable (e.g. $requiresJson = json_encode(...);) and if $requiresJson ===
false set $requiresJson = '[]' (or safe JSON string) before assigning to the
template, ensuring the template always receives valid JSON for the requires
variable.

- OwnerStoreController: requiresをPHP配列のまま渡すように修正
- plugin_confirm.twig: |raw を |json_encode_safe に変更

これによりTwig側の requires|length や {% for plugin in requires %}
が正しく配列として動作し、JavaScript出力も安全にエンコードされる

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Feb 24, 2026

Codecov Report

❌ Patch coverage is 90.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 78.84%. Comparing base (37c5250) to head (fdad301).
⚠️ Report is 21 commits behind head on 4.3.

Files with missing lines Patch % Lines
...be/Controller/Admin/Store/OwnerStoreController.php 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##                4.3    #6635   +/-   ##
=========================================
  Coverage     78.84%   78.84%           
- Complexity     6631     6632    +1     
=========================================
  Files           475      475           
  Lines         26539    26542    +3     
=========================================
+ Hits          20924    20928    +4     
+ Misses         5615     5614    -1     
Flag Coverage Δ
Unit 78.84% <90.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[セキュリティ] XSS脆弱性: 複数のテンプレートおよびコントローラーで不十分なエスケープ処理

1 participant