Skip to content

Commit d4064ee

Browse files
k-ibarakiclaude
andcommitted
security: move data size validation before merged cell cache build
Move data size validation before _build_merged_cell_cache to prevent DoS via memory exhaustion when large ranges (e.g., "A1:XFD1048576") are requested. This ensures resource-intensive processing only occurs for ranges within allowed limits. Security impact: - Prevents OOM attacks via massive merged cell iteration - Validates range size before expensive cache operations - Maintains existing DoS protection for frozen_rows Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 52ee242 commit d4064ee

1 file changed

Lines changed: 41 additions & 38 deletions

File tree

src/sharepoint_excel.py

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -461,18 +461,8 @@ def _parse_sheet(
461461
header_range, effective_range
462462
)
463463

464-
# マージセル情報をキャッシュ(パフォーマンス最適化)
465-
# 計算済みのeffective_range(effective_range_for_merge)を渡してキャッシュを構築し、
466-
# 戻り値としてmerged_ranges(結合セル範囲の一覧)を取得することで重複計算を回避
467-
merged_cell_map, merged_anchor_value_map, merged_ranges = (
468-
self._build_merged_cell_cache(sheet, effective_range_for_merge)
469-
)
470-
471-
# ここは「結合セルがある時だけ」返す
472-
if merged_ranges:
473-
sheet_data["merged_ranges"] = merged_ranges
474-
475-
# セル範囲のデータサイズ検証とデータ取得
464+
# データサイズ検証(DoS対策)
465+
# マージセルキャッシュ構築前に検証することで、巨大な範囲によるメモリ枯渇を防ぐ
476466
all_rows = []
477467

478468
if cell_range:
@@ -490,32 +480,6 @@ def _parse_sheet(
490480
f"例: cell_range='A1:Z1000'"
491481
)
492482

493-
else:
494-
# ヘッダー自動追加(include_frozen_rows=Trueの場合)
495-
# header_rangeは既に計算済み(L456付近)なので再利用
496-
if header_range:
497-
# ヘッダー範囲を取得
498-
header_data = sheet[header_range]
499-
header_rows = self._normalize_range_data(header_data)
500-
all_rows.extend(
501-
self._parse_rows(
502-
header_rows,
503-
merged_cell_map,
504-
merged_anchor_value_map,
505-
)
506-
)
507-
508-
# 通常のセル範囲取得(データ範囲)
509-
range_data = sheet[effective_range]
510-
rows_to_process = self._normalize_range_data(range_data)
511-
all_rows.extend(
512-
self._parse_rows(
513-
rows_to_process,
514-
merged_cell_map,
515-
merged_anchor_value_map,
516-
)
517-
)
518-
519483
elif sheet.dimensions:
520484
# シート全体を取得
521485
# データサイズ検証(DoS対策)
@@ -531,6 +495,45 @@ def _parse_sheet(
531495
f"例: cell_range='A1:Z1000'"
532496
)
533497

498+
# データサイズ検証後にマージセル情報をキャッシュ(パフォーマンス最適化 + DoS対策)
499+
# 計算済みのeffective_range(effective_range_for_merge)を渡してキャッシュを構築し、
500+
# 戻り値としてmerged_ranges(結合セル範囲の一覧)を取得することで重複計算を回避
501+
merged_cell_map, merged_anchor_value_map, merged_ranges = (
502+
self._build_merged_cell_cache(sheet, effective_range_for_merge)
503+
)
504+
505+
# ここは「結合セルがある時だけ」返す
506+
if merged_ranges:
507+
sheet_data["merged_ranges"] = merged_ranges
508+
509+
# データ取得
510+
if cell_range:
511+
# ヘッダー自動追加(include_frozen_rows=Trueの場合)
512+
# header_rangeは既に計算済みなので再利用
513+
if header_range:
514+
# ヘッダー範囲を取得
515+
header_data = sheet[header_range]
516+
header_rows = self._normalize_range_data(header_data)
517+
all_rows.extend(
518+
self._parse_rows(
519+
header_rows,
520+
merged_cell_map,
521+
merged_anchor_value_map,
522+
)
523+
)
524+
525+
# 通常のセル範囲取得(データ範囲)
526+
range_data = sheet[effective_range]
527+
rows_to_process = self._normalize_range_data(range_data)
528+
all_rows.extend(
529+
self._parse_rows(
530+
rows_to_process,
531+
merged_cell_map,
532+
merged_anchor_value_map,
533+
)
534+
)
535+
536+
elif sheet.dimensions:
534537
# 全データを取得
535538
rows_to_process = tuple(sheet.iter_rows())
536539

0 commit comments

Comments
 (0)