Skip to content

Commit 762a50e

Browse files
authored
Merge pull request #49 from ncdcdev/feat/strengthen-frozen-rows-dos-mitigation
Clarify frozen_rows validation behavior and improve logging
2 parents aed2757 + 13c04d6 commit 762a50e

2 files changed

Lines changed: 94 additions & 3 deletions

File tree

src/sharepoint_excel.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,12 @@ def _parse_sheet(
418418
frozen_rows, frozen_cols = self._get_frozen_panes(sheet)
419419

420420
# frozen_rows検証(DoS対策)
421+
# frozen_rowsは補助的なメタ情報なので、上限超過時はリセットして処理を続行
421422
if frozen_rows > config.excel_max_frozen_rows:
422423
logger.warning(
423-
"固定行数が上限を超えたため、freeze_panesを無視します "
424-
"(max_rows=%s, frozen_rows=%s, sheet=%s)",
424+
"固定行数が上限(%d)を超えたため、freeze_panes情報を無視します。"
425+
"ファイルの解析は続行されますが、ヘッダー自動追加機能は利用できません。"
426+
" (frozen_rows=%d, sheet=%s)",
425427
config.excel_max_frozen_rows,
426428
frozen_rows,
427429
sheet.title,

tests/test_sharepoint_excel.py

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from openpyxl import Workbook, load_workbook
99
from openpyxl.styles import Font, PatternFill
1010
from openpyxl.utils.exceptions import InvalidFileException
11-
from openpyxl.worksheet.views import Pane
11+
from openpyxl.worksheet.views import Pane, SheetView
1212

1313
from src.sharepoint_excel import SharePointExcelParser
1414

@@ -1300,3 +1300,92 @@ def test_include_frozen_rows_with_merged_cells(self):
13001300
assert sheet_data["rows"][0][0]["coordinate"] == "A1"
13011301
assert sheet_data["rows"][0][0]["merged"]["is_top_left"] is True
13021302
assert sheet_data["rows"][0][0]["merged"]["range"] == "A1:B1"
1303+
1304+
def test_frozen_rows_dos_mitigation_exceeds_limit(self):
1305+
"""frozen_rowsが上限を超えた場合は0にリセットされて処理が続行されること(DoS対策)"""
1306+
# 異常に大きなfrozen_rowsを持つExcelファイルを作成
1307+
wb = Workbook()
1308+
ws = wb.active
1309+
ws.title = "TestSheet"
1310+
ws["A1"] = "Header"
1311+
ws["A2"] = "Data"
1312+
1313+
# 異常に大きなfrozen_rowsを設定(openpyxl内部でpane.ySplitに直接設定)
1314+
pane = Pane()
1315+
pane.ySplit = 200 # デフォルト上限(100)を超える値
1316+
pane.xSplit = 0
1317+
pane.topLeftCell = "A201"
1318+
pane.state = "frozen"
1319+
1320+
sheet_view = SheetView(pane=pane)
1321+
ws.views.sheetView[0] = sheet_view
1322+
1323+
excel_bytes = BytesIO()
1324+
wb.save(excel_bytes)
1325+
excel_bytes.seek(0)
1326+
1327+
self.mock_download_client.download_file.return_value = excel_bytes.getvalue()
1328+
1329+
parser = SharePointExcelParser(self.mock_download_client)
1330+
1331+
# frozen_rowsが上限を超えているが、リセットされて処理が続行される
1332+
result_json = parser.parse_to_json("test.xlsx")
1333+
result = json.loads(result_json)
1334+
1335+
# frozen_rowsが0にリセットされている
1336+
sheet_data = result["sheets"][0]
1337+
assert sheet_data["frozen_rows"] == 0
1338+
assert sheet_data["frozen_cols"] == 0
1339+
# freeze_panes情報は含まれない
1340+
assert "freeze_panes" not in sheet_data
1341+
1342+
@patch("src.sharepoint_excel.config.excel_max_frozen_rows", 50)
1343+
def test_frozen_rows_dos_mitigation_within_limit(self):
1344+
"""frozen_rowsが上限以内の場合は正常に処理されること"""
1345+
# 上限以内のfrozen_rowsを持つExcelファイルを作成
1346+
excel_bytes = self._create_frozen_rows_excel("A3") # frozen_rows=2
1347+
self.mock_download_client.download_file.return_value = excel_bytes
1348+
1349+
parser = SharePointExcelParser(self.mock_download_client)
1350+
1351+
# 正常に処理される(エラーが発生しない)
1352+
result_json = parser.parse_to_json("test.xlsx")
1353+
result = json.loads(result_json)
1354+
1355+
# frozen_rowsが正しく取得されている
1356+
sheet_data = result["sheets"][0]
1357+
assert sheet_data["frozen_rows"] == 2
1358+
1359+
@patch("src.sharepoint_excel.config.excel_max_frozen_rows", 150)
1360+
def test_frozen_rows_dos_mitigation_custom_limit(self):
1361+
"""カスタム上限値が正しく適用されること"""
1362+
# カスタム上限(150)以内のfrozen_rowsを持つExcelファイルを作成
1363+
wb = Workbook()
1364+
ws = wb.active
1365+
ws.title = "TestSheet"
1366+
ws["A1"] = "Header"
1367+
1368+
# frozen_rows=120を設定(カスタム上限150以内)
1369+
pane = Pane()
1370+
pane.ySplit = 120
1371+
pane.xSplit = 0
1372+
pane.topLeftCell = "A121"
1373+
pane.state = "frozen"
1374+
1375+
sheet_view = SheetView(pane=pane)
1376+
ws.views.sheetView[0] = sheet_view
1377+
1378+
excel_bytes = BytesIO()
1379+
wb.save(excel_bytes)
1380+
excel_bytes.seek(0)
1381+
1382+
self.mock_download_client.download_file.return_value = excel_bytes.getvalue()
1383+
1384+
parser = SharePointExcelParser(self.mock_download_client)
1385+
1386+
# カスタム上限(150)以内なので正常に処理される
1387+
result_json = parser.parse_to_json("test.xlsx")
1388+
result = json.loads(result_json)
1389+
1390+
sheet_data = result["sheets"][0]
1391+
assert sheet_data["frozen_rows"] == 120

0 commit comments

Comments
 (0)