mcpField
S
sentry-backend-bugs

sentry-backend-bugs

github.com/getsentryテスト・品質45.7k84 回インストール今日更新
リポジトリ

sentry-backend-bugs について

本番で確認された不具合パターンに基づき、Sentry のバックエンド変更をレビューするスキルです。関連するコード経路を追跡し、中・高確度の指摘と修正案または確認事項を報告します。

主な機能

  1. 01不具合カテゴリ別のコード確認
  2. 02呼び出しとデータフローの追跡
  3. 03実行時・データ処理の問題を網羅
  4. 04中・高確度の指摘のみ報告
  5. 05安全な処理パターンの提示

活用シーン

  1. 01ブランチ内のバックエンド変更のレビュー
  2. 02ORM クエリのレコード欠落リスク確認
  3. 03入力解析とデータベース整合性の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/getsentry/sentry --skill sentry-backend-bugs

インストール後、Claude Code で/sentry-backend-bugsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
references/
concurrency-bugs.md
data-validation.md
database-integrity.md
integration-errors.md
missing-records.md
null-and-type-errors.md
query-validation.md
url-safety.md

詳細

Sentry バックエンドのバグパターンレビュー

最も多くの実際の本番エラーを引き起こすパターンをチェックすることで、Sentry バックエンドコードのバグを見つけます。

このスキルは、638 件の実際の本番イシュー(解決済み 393 件、未解決 220 件、無視 25 件)から得られたパターンをエンコードしたもので、65,000 人以上の影響を受けたユーザーにわたって 2,700 万件を超えるエラーイベントを生成しています。これらは理論上のリスクではありません。最も頻繁に出荷される実際のバグであり、解決済みイシューからの既知の修正方法があります。

スコープ

ユーザー、Warden、または現在のブランチ diff から提供されたコードをレビューします。ユーザーが対象を指定しない場合は、現在のブランチ diff をレビューします。変更されたハンクまたはファイルから開始し、動作を確認するために必要な範囲でのみ外側へ読み進めます。

  1. 変更されたコードを以下のパターンチェックと照らし合わせて分析します。
  2. 必要に応じて、Read と Grep を使用して最初の diff を超えてデータフローを追跡します。動作が確認できるまで、関数呼び出し、呼び出し元、シリアライザー、タスク、ORM の境界をたどります。
  3. HIGH と MEDIUM の確信度の検出結果のみを報告します。
確信度基準アクション
HIGHコードパスを追跡し、パターンが既知のバグクラスと一致することを確認済み修正方法とともに報告
MEDIUMパターンは存在するが、文脈によって緩和される可能性がある検証が必要として報告
LOW理論上、または他の場所で緩和されている報告しない

ステップ 1: コードを分類する

レビュー対象を判断し、関連するリファレンスを読み込みます。

コードの種類読み込むリファレンス
ORM クエリ、モデル検索、.objects.get()、FK アクセスreferences/missing-records.md
型変換、None の処理、オプションの読み取り、シリアライザーの戻り値references/null-and-type-errors.md
データ入力のパース、フィールド長、リクエストボディ、伸長references/data-validation.md
get_or_create、save()、一意制約、整数オーバーフローreferences/database-integrity.md
統合 Webhook、外部 API 呼び出し、SentryApp フックreferences/integration-errors.md
Dict のイテレーション、共有状態、同時アクセスreferences/concurrency-bugs.md
Snuba クエリ、メトリクスサブスクリプション、検索フィルターreferences/query-validation.md
リダイレクト URL、URL 構築、ルーティングreferences/url-safety.md

コードが複数のカテゴリにまたがる場合は、関連するすべてのリファレンスを読み込みます。

ステップ 2: 主要なバグパターンをチェックする

これらは、実際の本番データから得られた頻度と影響の組み合わせで並べられています。

チェック 1: メトリクスサブスクリプションクエリエラー -- 113 件のイシュー、3,035,640 イベント

対象データセットに存在しないタグや関数を参照するアラートおよびメトリクスサブスクリプション。これらは作成されると継続的に発火します。

危険な兆候:

  • 検証せずにユーザー提供のクエリ文字列を使用して SubscriptionData で Snuba サブスクリプションを作成する
  • メトリクスデータセット上の p95/p99 関数で transaction.duration を参照する(そこでは文字列型です)
  • カスタムタグ名(例: customerType)が存在するか確認せずにフィルターディメンションとして使用する
  • データセットがしきい値パラメータをサポートしているか確認せずに resolve_apdex_function を呼び出す

安全なパターン:

  • サブスクリプション作成前にクエリフィールドをデータセットスキーマと照合して検証する
  • _create_in_snuba 呼び出しを try/except SubscriptionError でラップし、サブスクリプションを無効としてマークする
  • メトリクスサブスクリプションクエリを構築する前に IncompatibleMetricsQuery チェックを使用する
チェック 2: レコードの欠落 / 古い参照 -- 81 件の問題、1,403,592 イベント

コードがレコードの存在を前提として Django モデルに対して .get() を呼び出していますが、そのレコードは削除、マージ、または一度も作成されていません。

危険なパターン:

  • DoesNotExist に対する try/except なしの Model.objects.get(id=some_id)
  • ワークフローエンジンにおける、削除処理なしの Detector.objects.get(id=detector_id)
  • モニター / cron コンシューマーにおける Environment.objects.get(name=env_name)
  • 請求タスクにおける Subscription.objects.get(id=sub_id)
  • Snuba クエリ結果の ID を伴う Group.objects.get() の使用 (グループは削除またはマージされている可能性があります)
  • 2 番目の .get() が失敗する連鎖ルックアップ

安全なパターン:

  • None チェックを伴う Model.objects.filter(...).first()
  • グレースフルなフォールバック (404、スキップ、ログ) を返す DoesNotExist の try/except
  • .get() の前の Queryset .exists() チェック
  • API エンドポイントでは、DoesNotExist には 404 を、検証エラーには 400 を返します。意図的に 500 を返すことを提案しないでください。

バグではない — 報告しないでください:

  • インフラストラクチャの不変条件: .get() がデプロイの前提条件を強制する場合 (例: 「シングル組織モードではデフォルト組織が存在しなければならない」)、クラッシュするのが正しい動作です — 500 はコードの欠陥ではなく、設定ミスを示します。
  • 親による検証済み: エンドポイントの基底クラスがオブジェクトを検証している場合 (例: OrganizationEndpoint が組織を解決する)、真の競合や削除の時間枠が存在しない限り、関連レコードに対する .get() を報告しないでください。報告する前にエンドポイントの親クラスを確認してください。
  • 設定のルックアップ: 必須の設定オブジェクトを読み込むコード (get_default()、設定ベースのルックアップ) は、設定が誤っている場合にハードに失敗することが想定されています。
チェック 3: 検索クエリの検証 -- 57 件の問題、2,001,330 イベント

ユーザー提供またはサブスクリプションに保存されたクエリフィルターが、無効な値、削除されたイシュー、または未解決のタグを参照することによる InvalidSearchQuery。

危険なパターン:

  • 削除または名前変更されたプロジェクトを参照する保存済みサブスクリプションからのショート ID を伴う by_qualified_short_id_bulk() の呼び出し
  • ユーザー提供のイシューショート ID に対して Group.objects.get() を呼び出す _issue_filter_converter
  • 対象データセットにタグが存在するかを確認せずに呼び出される resolve_tag_key()
  • アラートルールのサブスクリプションからの未検証の query 文字列を Snuba に渡す

安全なパターン:

  • by_qualified_short_id_bulk() に渡す前にショート ID を検証する -- Group.DoesNotExist を処理する
  • _issue_filter_converter を try/except でラップし、無効なフィルターに対して空の結果を返す
  • データセットのスキーマに対してタグの存在を事前検証する
チェック 4: 値の検証エラー -- 45 件の問題、1,000,624 イベント

不十分な入力検証による ValueError: アンパックエラー、無効な enum 値、期待されるオブジェクトの欠落、および Pydantic/DRF の検証失敗。

危険なパターン:

  • 結果が正確に 1 件存在すると仮定したアクションビルダーにおける SentryAppInstallation.objects.get()
  • DoesNotExist を処理せずに ID で検索する AlertRuleWorkflow
  • 区切り文字がより少ない可能性がある文字列に対するタプルのアンパック (a, b, c = value.split(":"))
  • ValueError を捕捉せずに行う整数 enum のルックアップ (例: DetectorPriorityLevel(value))

安全なパターン:

  • .get() の前に期待される件数を検証する: .filter() を使用して .count() を確認する
  • タプルのアンパックを try/except ValueError でラップするか、事前に長さを検証する
  • ユーザー向けの enum 変換には try: EnumClass(value) except ValueError: を使用する
  • API エンドポイントでは、ValueError および検証失敗に対して 400 を返します。
チェック 5: 型エラー -- 28 件の問題、740,106 イベント

関数に誤った型が渡されています。イテラブルでないものに対する反復、無効な dict キー、オブジェクトが期待される箇所での None などです。

危険信号:

  • orjson.dumps(payload) で、payload の dict に文字列以外のキーが含まれる可能性がある
  • list(setting) で、setting が int である可能性がある (project.get_option() から取得)
  • result["key"] = value で、result が None である可能性がある
  • DB/設定から取得した値が list ではなく int や None である可能性があるのに反復している

安全なパターン:

  • 反復の前に型チェック: list(value) の前に isinstance(value, (list, str))
  • JSON シリアライズの前に dict のキーを検証: すべてのキーが文字列であることを確認する
  • 添字代入の前に None ガード: if result is not None:
  • 型チェックを伴う防御的なオプション読み取り
  • API エンドポイントでは: ユーザー入力からの型の不一致に対して 400 を返す。
チェック 6: 内部 API リクエストエラー -- 71 件の問題、829,753 イベント

サービス間の内部 Sentry API 呼び出しによる ApiError で、多くの場合、古いサブスクリプションパラメータが原因です。

危険信号:

  • メトリックアラートのチャート描画における api.client.get() 呼び出しで、400 レスポンスを処理していない
  • 古いサブスクリプションクエリ (削除されたタグ/メトリクスを参照) を転送する内部 API 呼び出し
  • incidents/charts.py 内の fetch_metric_alert_events_timeseries() で ApiError を処理していない

安全なパターン:

  • 内部 API 呼び出しを try/except ApiError でラップし、穏当なフォールバックを返す
  • 内部 API リクエストを行う前にクエリパラメータを検証する
  • チャート/可視化コードで 400/500 レスポンスを明示的に処理する
チェック 7: データベース制約違反 -- 22 件の問題、2,962,198 イベント

整数オーバーフロー、外部キー違反、一意制約違反による IntegrityError と DataError です。

危険信号:

  • 上限を設けずに times_seen カウンタをインクリメント (約 21 億で整数オーバーフロー)
  • MonitorIncident からの FK 制約を処理せずに MonitorCheckIn レコードを削除
  • 下限が上限を超える可能性がある GroupOpenPeriod の日付範囲に対する UPDATE
  • 一意制約を持つモデルに対する save() で IntegrityError を処理していない

安全なパターン:

  • 更新前に整数フィールドを上限で制限: 32 ビット int カラムには min(value, 2_147_483_647)
  • 一括削除の前に CASCADE を使用するか FK 制約を処理する
  • 保存前に日付範囲の境界を検証する
  • try/except IntegrityError で get() または update_or_create() にフォールバックする
チェック 8: データのパースとデシリアライズ -- 19 件の問題、272,812 イベント

外部データや保存された圧縮データをパースする際の JSONDecodeError と ZstdError です。

危険信号:

  • JSONDecodeError を捕捉せずに json.loads(response.body)
  • 破損したフレーム記述子を処理せずに zstd.decompress(data)
  • 切り詰められている可能性がある VSTS/Azure DevOps Webhook ボディのパース
  • API レスポンスが常に有効な JSON であると仮定する (HTML エラーページである可能性がある)

安全なパターン:

  • JSON/圧縮を常に try/except でラップし、穏当なフォールバックを用意する
  • パースの前に content-type ヘッダーを確認する
  • ボディをパースする前にレスポンスステータスを検証する
チェック 9: キーアクセスの欠落 (KeyError) -- 25 件の問題、155,020 イベント

存在チェックなしで辞書のキーや HTTP ヘッダーにアクセスしています。

危険信号:

  • Webhook ハンドラにおける request.META["HTTP_X_GITLAB_TOKEN"] (ヘッダーが存在しない可能性がある)
  • Bitbucket Webhook ハンドラにおける request.META["HTTP_X_EVENT_KEY"]
  • イベントタイプが登録されているかを確認せずに HANDLERS[event_type] で辞書のキーを検索
  • 可変長の分割を伴うヘッダー値からのタプル展開

安全なパターン:

  • None チェックを伴う request.META.get("HTTP_X_GITLAB_TOKEN")
  • 未知のイベントタイプに対するフォールバックを伴う HANDLERS.get(event_type)
  • 処理の前に Webhook ハンドラの先頭で必須ヘッダーを検証する
チェック 10: 並行性とランタイムのバグ -- 23 件の問題、38,443 イベント

イテレーション中の辞書の変更、共有された可変状態、および未実装のコードパス。

危険信号:

  • 別のスレッドが変更している最中の for key in self._dict: (RuntimeError)
  • 無制限に成長する共有 KafkaPublisher 辞書への発行
  • 別のスレッドでイテレーションされている辞書に対する dict.pop() または dict[key] = value
  • 新しい検索式タイプに対する NotImplementedError ハンドラーの欠如

安全なパターン:

  • イテレーション前の dict.copy()
  • 共有された可変状態には threading.Lock を使用する
  • 機能を有効化する前にすべてのコードパスを実装する
  • 変更が必要な場合の安全なイテレーションには list(dict.keys()) を使用する
チェック 11: ロジックの正しさ -- パターンベースではない

上記のすべての既知のパターンをチェックした後、変更されたコード自体について推論します:

  • すべてのコードパスは正しい型を返しますか?
  • 条件分岐のすべてのブランチが処理されていますか (特に else / デフォルトケース)?
  • 任意の入力 (None、空のリスト、0、空の文字列) が予期しない動作を引き起こす可能性はありますか?
  • ループ、スライス、または範囲チェックに off-by-one エラーはありますか?
  • このコードが並行して実行される場合、共有状態は保護されていますか?

バグを引き起こす特定の入力を追跡できる場合にのみ報告してください。理論上の懸念を報告しないでください。

バグではない — フラグを立てないでください:

  • インフラストラクチャの不変条件を強制する assert ステートメント — Sentry は python -O で実行されないため、アサーションは常にアクティブです。違反された不変条件でクラッシュすることは意図的です。
  • 投機的な入力の懸念 (例: 「この URL は長すぎる可能性がある」、「このヘッダーは不正な形式である可能性がある」) — ただし、入力が実際に検証されずにコードパスに到達することを示せる場合を除きます。報告する前に既存の検証 (ホストチェック、スキーマ検証、DRF シリアライザー) を確認してください。

チェックで潜在的な発見が得られなかった場合は、停止してゼロ件の発見を報告してください。レポートを埋めるために問題をでっち上げないでください。コードにこれらのパターンに一致するバグがない場合、空の結果が正しい出力です。

各コードの場所は、最も具体的に一致するパターンの下で一度だけ報告する必要があります。同じ行を複数のチェックの下でフラグを立てないでください。

ステップ 3: 発見の報告

各発見について、レビューハーネスが必要とする証拠を提供してください:

  • 正確な場所
  • 重大度と信頼度
  • 具体的なトリガーとなる入力または状態
  • 根本原因と結果
  • 利用可能な場合の一致する本番環境の前例
  • 具体的なコード修正、ハーネスがサポートしている場合は統一 diff として

修正提案には実際のコードを含める必要があります。修正としてコメントや docstring を提案しないでください。

API エンドポイントでの修正を提案する場合、適切な HTTP ステータスコードを使用してください (見つからない場合は 404、不正な入力の場合は 400、競合の場合は 409)。意図的に 500 を返すことを提案しないでください。

独自の出力形式を規定しないでください — レビューハーネスがレスポンス構造を制御します。

コメント