171 lines
5.3 KiB
Python
171 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
檔案上傳驗證測試腳本
|
|
測試 app.py 中的檔案上傳驗證函數
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
from io import BytesIO
|
|
|
|
# 確保可以導入專案模組
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from app import allowed_file, validate_upload_file
|
|
from werkzeug.datastructures import FileStorage
|
|
|
|
def test_allowed_file():
|
|
"""測試檔案副檔名驗證"""
|
|
print("="*60)
|
|
print("測試 1: 檔案副檔名驗證 (allowed_file)")
|
|
print("="*60)
|
|
|
|
test_cases = [
|
|
# (檔案名, 是否應該通過, 描述)
|
|
('report.xlsx', True, 'Excel 2007+ 格式'),
|
|
('data.xls', True, 'Excel 97-2003 格式'),
|
|
('export.csv', True, 'CSV 格式'),
|
|
('REPORT.XLSX', True, '大寫副檔名'),
|
|
('file.XLS', True, '混合大小寫'),
|
|
('malware.exe', False, '執行檔'),
|
|
('script.php', False, 'PHP 腳本'),
|
|
('shell.sh', False, 'Shell 腳本'),
|
|
('image.jpg', False, '圖片檔'),
|
|
('document.pdf', False, 'PDF 檔案'),
|
|
('archive.zip', False, '壓縮檔'),
|
|
('file', False, '無副檔名'),
|
|
('', False, '空檔名'),
|
|
('.xlsx', False, '只有副檔名'),
|
|
('../../etc/passwd.csv', True, '路徑遍歷但副檔名正確'),
|
|
('file.csv.exe', False, '雙副檔名攻擊'),
|
|
]
|
|
|
|
passed = 0
|
|
failed = 0
|
|
|
|
for filename, should_pass, description in test_cases:
|
|
result = allowed_file(filename)
|
|
|
|
if result == should_pass:
|
|
print(f"✅ 通過: {description} | 檔名: '{filename}' | 結果: {result}")
|
|
passed += 1
|
|
else:
|
|
print(f"❌ 失敗: {description} | 檔名: '{filename}' | 期望: {should_pass} | 實際: {result}")
|
|
failed += 1
|
|
|
|
assert failed == 0
|
|
|
|
def test_validate_upload_file():
|
|
"""測試完整的檔案上傳驗證"""
|
|
print("\n" + "="*60)
|
|
print("測試 2: 完整檔案上傳驗證 (validate_upload_file)")
|
|
print("="*60)
|
|
|
|
test_cases = [
|
|
# (檔名, 是否應該通過, 描述)
|
|
('即時業績(全月).xlsx', True, '正常中文檔名'),
|
|
('sales_report.csv', True, '正常英文檔名'),
|
|
('data 2024.xls', True, '包含空格的檔名'),
|
|
('../../../etc/passwd.csv', False, '路徑遍歷攻擊'),
|
|
('../../config.py.xlsx', False, '路徑遍歷攻擊'),
|
|
('malware.exe', False, '不允許的副檔名'),
|
|
('', False, '空檔名'),
|
|
('<script>alert(1)</script>.xlsx', True, 'XSS 嘗試(會被清理)'),
|
|
('file|pipe.csv', True, '特殊字元(會被清理)'),
|
|
('file\x00null.xlsx', True, 'Null 字元(會被清理)'),
|
|
]
|
|
|
|
passed = 0
|
|
failed = 0
|
|
|
|
for filename, should_pass, description in test_cases:
|
|
# 建立模擬的 FileStorage 物件
|
|
fake_file = FileStorage(
|
|
stream=BytesIO(b"test data"),
|
|
filename=filename,
|
|
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
)
|
|
|
|
is_valid, error_msg, safe_name = validate_upload_file(fake_file)
|
|
|
|
if is_valid == should_pass:
|
|
print(f"✅ 通過: {description}")
|
|
print(f" 原始檔名: '{filename}'")
|
|
if is_valid:
|
|
print(f" 安全檔名: '{safe_name}'")
|
|
else:
|
|
print(f" 拒絕原因: {error_msg}")
|
|
passed += 1
|
|
else:
|
|
print(f"❌ 失敗: {description}")
|
|
print(f" 原始檔名: '{filename}'")
|
|
print(f" 期望: {'通過' if should_pass else '拒絕'}")
|
|
print(f" 實際: {'通過' if is_valid else '拒絕'}")
|
|
if not is_valid:
|
|
print(f" 錯誤訊息: {error_msg}")
|
|
failed += 1
|
|
print()
|
|
|
|
assert failed == 0
|
|
|
|
def test_secure_filename_cleaning():
|
|
"""測試 secure_filename 的清理效果"""
|
|
print("="*60)
|
|
print("測試 3: 檔案名稱清理效果")
|
|
print("="*60)
|
|
|
|
from werkzeug.utils import secure_filename
|
|
|
|
test_cases = [
|
|
('即時業績(全月).xlsx', '中文與括號'),
|
|
('../../../etc/passwd.csv', '路徑遍歷'),
|
|
('file|pipe<>:*.csv', '特殊字元'),
|
|
(' spaces .xlsx', '空格'),
|
|
('file\x00null.csv', 'Null 字元'),
|
|
('<script>alert(1)</script>.xlsx', 'HTML 標籤'),
|
|
]
|
|
|
|
print("\n檔案名稱清理範例:")
|
|
print("-"*60)
|
|
|
|
for original, description in test_cases:
|
|
cleaned = secure_filename(original)
|
|
print(f"描述: {description}")
|
|
print(f" 原始: '{original}'")
|
|
print(f" 清理後: '{cleaned}'")
|
|
print()
|
|
|
|
assert True # 這個測試只是展示,不計入通過/失敗
|
|
|
|
def main():
|
|
"""主測試函數"""
|
|
print("="*60)
|
|
print("MOMO 監控系統 - 檔案上傳驗證測試")
|
|
print("="*60)
|
|
print()
|
|
|
|
# 執行所有測試
|
|
try:
|
|
test_allowed_file()
|
|
test_validate_upload_file()
|
|
except AssertionError:
|
|
print("\n⚠️ 測試未通過,請檢查!")
|
|
return 1
|
|
|
|
# 展示清理效果(不計入結果)
|
|
test_secure_filename_cleaning()
|
|
|
|
# 顯示總結
|
|
print("="*60)
|
|
print("測試結果摘要")
|
|
print("="*60)
|
|
print("✅ 通過: 測試函式完成")
|
|
print("❌ 失敗: 0")
|
|
|
|
print("\n🎉 所有檔案上傳驗證測試通過!")
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|