Skip to content

特定のURL / ドメインのリンクカードが文字化けするバグを修正#9778

Open
mousu-a wants to merge 2 commits intomainfrom
bug/fix-link-card-encoding
Open

特定のURL / ドメインのリンクカードが文字化けするバグを修正#9778
mousu-a wants to merge 2 commits intomainfrom
bug/fix-link-card-encoding

Conversation

@mousu-a
Copy link
Copy Markdown
Contributor

@mousu-a mousu-a commented Mar 12, 2026

Issue

概要

一部URL / ドメインのリンクカードが文字化けする問題を修正しました。

変更確認方法

  1. bug/fix-link-card-encodingをローカルに取り込む
  2. bin/devでサーバーを起動
  3. 日報を作成(ログインしていなければログイン 誰でも良いです)
  4. 以下テキストを"内容"に貼り付けする
@[card](https://www.youtube.com/watch?v=8LudKmk7yPM) 
  1. プレビューにて、リンクカードが正常に展開されることを確認

Screenshot

変更前

スクリーンショット 2026-03-26 10 35 46

変更後

image

Summary by CodeRabbit

リリースノート

  • 改善

    • HTTPS通信の取り扱いを強化し、SSL証明書の検証と応答の自動エンコーディング処理に対応しました(より安全で文字化けの少ない通信)。
  • テスト

    • 特定ドメインのリンクカード表示に関するシステムテストを追加し、YouTubeリンクを含む投稿でタイトル等が正しく表示されることを確認するケースを導入しました。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

Metadata#fetchNet::HTTP.get_responseから、ホストとポートで手動構築したNet::HTTPインスタンスを使う実装に変更され、HTTPSでのuse_ssl設定、証明書検証(VERIFY_PEER)とresponse_body_encoding = trueを有効化し、http.request_get(@uri.request_uri)で応答を取得するようになりました。テストにYouTubeリンクの表示を検証するシステムテストが追加されました。

変更内容

Cohort / File(s) Summary
メタデータHTTP処理
app/models/metadata.rb
Net::HTTP.get_responseからNet::HTTPインスタンスを明示的に構築してリクエストを送信する実装へ変更。HTTPS時にuse_ssl = trueOpenSSL::SSL::VERIFY_PEERを設定し、response_body_encoding = trueを有効化。レスポンス読み取りをhttp.request_get(@uri.request_uri)に移行。
システムテスト追加
test/system/markdown_test.rb
YouTubeリンクを含む報告でリンクカードが文字化けせず正しくレンダリングされることを確認するシステムテストケースを追加(.a-link-card__titleの存在と特定テキストを検証)。

推定コードレビュー工数

🎯 3 (Moderate) | ⏱️ ~20分

関連する可能性のあるイシュー

関連する可能性のあるプルリクエスト

推奨レビュアー

  • komagata

ポエム

🐰 ぽこぽこネットを渡り、
小さなHTTPもふわり整え、
証書は固く、文字は澄み、
リンクカードもにっこりだよ✨
守るよ、みんなのページを。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed プルリクエストのタイトルは「特定のURL / ドメインのリンクカードが文字化けするバグを修正」で、変更内容の主要な目的(文字化けバグの修正)を明確に表現しており、簡潔かつ具体的です。
Description check ✅ Passed プルリクエストの説明は、Issue番号、概要、変更確認方法、変更前後のスクリーンショットを含む必須セクションがすべて完備されており、テンプレートに準拠しています。

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bug/fix-link-card-encoding

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.

@mousu-a mousu-a force-pushed the bug/fix-link-card-encoding branch from 7668893 to 7c33520 Compare March 26, 2026 01:24
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
http.response_body_encoding = true
Copy link
Copy Markdown
Contributor Author

@mousu-a mousu-a Mar 26, 2026

Choose a reason for hiding this comment

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

http.response_body_encoding = trueとすることで、encodingをHTMLから推測してくれるようになる。

https://github.com/ruby/ruby/blob/master/lib/net/http.rb#L2450
https://github.com/ruby/ruby/blob/master/lib/net/http/response.rb#L372

@mousu-a mousu-a changed the title 特定のURLのリンクカードが文字化けするバグを修正 特定のURL / ドメインののリンクカードが文字化けするバグを修正 Mar 26, 2026
@mousu-a mousu-a changed the title 特定のURL / ドメインののリンクカードが文字化けするバグを修正 特定のURL / ドメインのリンクカードが文字化けするバグを修正 Mar 26, 2026
@mousu-a mousu-a marked this pull request as ready for review March 26, 2026 01:39
@github-actions github-actions bot requested a review from komagata March 26, 2026 01:39
Copy link
Copy Markdown

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/models/metadata.rb`:
- Around line 10-17: Metadata#fetch currently calls Net::HTTP#request_get
directly and can raise SSL/network exceptions; set sensible timeouts on the
Net::HTTP instance (open_timeout and read_timeout) and wrap the request in a
rescue that catches Net::OpenTimeout, Net::ReadTimeout, SocketError,
OpenSSL::SSL::SSLError (and optionally StandardError as a fallback), log the
error via the existing logger, and return nil instead of letting the exception
propagate; update the code around http = Net::HTTP.new(...), http.use_ssl = true
and response = http.request_get(`@uri.request_uri`) to apply these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b28c683f-7157-41a5-85a1-a4984df4eff7

📥 Commits

Reviewing files that changed from the base of the PR and between ad44c68 and 7c33520.

📒 Files selected for processing (1)
  • app/models/metadata.rb

Comment on lines +10 to +17
http = Net::HTTP.new(@uri.host, @uri.inferred_port)
if @uri.scheme == 'https'
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
http.response_body_encoding = true

response = http.request_get(@uri.request_uri)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "metadata.rb" -type f | head -20

Repository: fjordllc/bootcamp

Length of output: 85


🏁 Script executed:

find . -name "card.rb" -type f | grep link_card

Repository: fjordllc/bootcamp

Length of output: 91


🏁 Script executed:

find . -name "metadata_controller.rb" -type f

Repository: fjordllc/bootcamp

Length of output: 105


🏁 Script executed:

cat -n ./app/models/metadata.rb | head -50

Repository: fjordllc/bootcamp

Length of output: 1619


🏁 Script executed:

cat -n ./app/models/link_card/card.rb | head -50

Repository: fjordllc/bootcamp

Length of output: 941


🏁 Script executed:

cat -n ./app/controllers/api/metadata_controller.rb | head -50

Repository: fjordllc/bootcamp

Length of output: 453


🏁 Script executed:

# Verify if there's any middleware or rescue_from that might catch these exceptions
rg -A 5 "rescue_from|exception_handler" app/controllers/api/metadata_controller.rb

Repository: fjordllc/bootcamp

Length of output: 43


🏁 Script executed:

# Check ApplicationController for any rescue_from
rg -A 5 "rescue_from" app/controllers/application_controller.rb | head -30

Repository: fjordllc/bootcamp

Length of output: 43


外部HTTP例外が未処理で、500エラーになる可能性があります

Line 17 の request_get は SSL/ネットワーク例外(Net::OpenTimeoutNet::ReadTimeoutSocketErrorOpenSSL::SSL::SSLError 等)を送出します。呼び出し元(app/models/link_card/card.rbapp/controllers/api/metadata_controller.rb)で rescue していないため、例外が伝播して 500 エラーになります。コントローラーは nil で 400 を返すことを想定しているため、Metadata#fetch 側でタイムアウト設定と例外ハンドリングを追加して nil で返してください。

修正案
 def fetch
   http = Net::HTTP.new(`@uri.host`, `@uri.inferred_port`)
   if `@uri.scheme` == 'https'
     http.use_ssl = true
     http.verify_mode = OpenSSL::SSL::VERIFY_PEER
   end
   http.response_body_encoding = true
+  http.open_timeout = 5
+  http.read_timeout = 5

   response = http.request_get(`@uri.request_uri`)
   response.message == 'OK' ? parse(response.body) : nil
+rescue OpenSSL::SSL::SSLError, SocketError, Errno::ECONNREFUSED,
+       Net::OpenTimeout, Net::ReadTimeout, EOFError, Timeout::Error
+  nil
 end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/models/metadata.rb` around lines 10 - 17, Metadata#fetch currently calls
Net::HTTP#request_get directly and can raise SSL/network exceptions; set
sensible timeouts on the Net::HTTP instance (open_timeout and read_timeout) and
wrap the request in a rescue that catches Net::OpenTimeout, Net::ReadTimeout,
SocketError, OpenSSL::SSL::SSLError (and optionally StandardError as a
fallback), log the error via the existing logger, and return nil instead of
letting the exception propagate; update the code around http =
Net::HTTP.new(...), http.use_ssl = true and response =
http.request_get(`@uri.request_uri`) to apply these changes.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

🚀 Review App

URL: https://bootcamp-pr-9778-fvlfu45apq-an.a.run.app

Basic認証: fjord / (ステージングと同じ)
PR更新時に自動で再デプロイされます。

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
test/system/markdown_test.rb (1)

131-132: アサーションをリンクカード要素に限定し、タイトル完全一致依存を緩めると堅牢です。

Line 132 の assert_text はページ全体を対象にするため、意図しない場所の同文言でも通る可能性があります。文字化け回帰の目的に合わせて、.a-link-card__title-text へのスコープ限定+部分一致にすると安定します。

差分案
-    assert_selector '.a-link-card__title'
-    assert_text '角谷トーク2023 本編'
+    assert_selector '.a-link-card__title'
+    assert_selector '.a-link-card__title-text', text: /角谷トーク2023/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/system/markdown_test.rb` around lines 131 - 132, 現在のテストはページ全体を対象にする
assert_text を使っており誤検出の原因になるため、`.a-link-card__title` 要素内の
`.a-link-card__title-text` にスコープを限定して部分一致で検証するよう修正してください: 検出には既存の
assert_selector '.a-link-card__title' を残し、続けて within('.a-link-card__title') または
find('.a-link-card__title') ブロック内で `.a-link-card__title-text` を対象に assert_text
'角谷トーク2023 本編' を部分一致(exact: false 相当のマッチ)で実行するように変更してください。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/system/markdown_test.rb`:
- Around line 131-132: 現在のテストはページ全体を対象にする assert_text
を使っており誤検出の原因になるため、`.a-link-card__title` 要素内の `.a-link-card__title-text`
にスコープを限定して部分一致で検証するよう修正してください: 検出には既存の assert_selector '.a-link-card__title'
を残し、続けて within('.a-link-card__title') または find('.a-link-card__title') ブロック内で
`.a-link-card__title-text` を対象に assert_text '角谷トーク2023 本編' を部分一致(exact: false
相当のマッチ)で実行するように変更してください。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 543dd44d-4049-44f9-8df3-916a84c9b1d7

📥 Commits

Reviewing files that changed from the base of the PR and between 7c33520 and 2d6e277.

📒 Files selected for processing (1)
  • test/system/markdown_test.rb

@mousu-a
Copy link
Copy Markdown
Contributor Author

mousu-a commented Mar 27, 2026

@komagata
お疲れ様です。
現在リンクカードのURLへのリクエストを自前で実装しているのですが、ここにGemを導入すべきか迷っておりkomagataさんの意見をお聞きしたいです。
GemはFaradayを検討しています。

https://github.com/lostisland/faraday

現状やらなければいけないと考えている要件は以下の通りです。

  • URLのセキュリティチェック(主にSSRF対策など)
    • ユーザー入力のURLにリクエストするので必要だと考えています。
  • URLへのリクエスト
  • response.bodyのエンコーディング設定
    • これがないと文字化けが発生するため必要になります。
  • URLのレスポンスがリダイレクト時のフォールバック処理、リダイレクト先URLのセキュリティチェック(主にSSRF対策)
    • リンクカードのURLが302 リダイレクトだった場合、さらにリダイレクト先にリクエストする必要があると考えています。(その際のセキュリティチェックも)
  • エラーハンドリング

Gemを導入するパターンでは、URLへのリクエストとエラーハンドリングはGemにやってもらえます。
ですがエンコーディング(HTMLを見てencodingを推測する)は自前で実装しなければいけなくなります。

自前で実装するパターンでは、URLへのリクエストとエラーハンドリングを自前で実装しなければいけなくなります。
その代わりエンコーディングが1行で済みます。

#9778 (comment)

以下の処理はどちらのパターンでも自前で実装する必要があります。

  • リダイレクト時のフォールバック処理(リダイレクト先URLにリクエスト、リダイレクトの回数制限など)
  • リダイレクト先URLのセキュリティチェック
  • URLのセキュリティチェック

自分の意見としては、Gemを導入し、URLへのリクエストとエラーハンドリングを任せ、セキュリティチェックやエンコーディング設定などのリンクカード特有の設定に関しては自前で実装するのが良いと考えているのですがどうでしょうか?

@komagata
Copy link
Copy Markdown
Member

@mousu-a なるほどです。

URLのセキュリティチェック

これは具体的には何をどうチェックする感じですか?

@mousu-a
Copy link
Copy Markdown
Contributor Author

mousu-a commented Mar 28, 2026

@komagata
すみません、質問の答えではないのですが(現在深掘りしています)、こちらでの連絡で申し訳ないのですがFBCを一時休会することになりました。
このPR(やissue作成、PR作成など)は引き続き続けても良かったでしょうか?
プログラミングをやめるわけではないので稼働自体は可能な(むしろやりたい)感じです。

@komagata
Copy link
Copy Markdown
Member

komagata commented Apr 1, 2026

@mousu-a OSSなのでPRをいただくことには問題ないです。

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.

2 participants