如何製作用於測試的損毀檔案
一個只見過完好檔案的驗證器,並沒有真正被測試過。以下說明如何取得一個刻意弄壞的檔案:它的大小恰好就是你要求的大小,並附帶一份清單,說明你的系統該如何處理它。
簡短回答
tfg generate --format png --size 2mb --damage zero-head --out ./out 會寫出一個恰好 2097152
位元組的 PNG,它開頭的幾個位元組是零,旁邊的清單則記錄你的系統應該拒絕它。
常見做法
為什麼手工弄壞的檔案不是好測試
常見做法是用十六進位編輯器、用指令碼翻轉幾個隨機位元組,或用 head 或 truncate 把檔案截短。這些做法能用一次,之後就要付出代價:
- 每次都不一樣。隨機位元組每次執行都落在新的位置,所以週二出現的失敗,週三可能不再出現。
- 它會改變大小。被截短的檔案比它本應低於的限制還要小,於是大小檢查先於內容檢查給出回答,測試因為錯誤的原因而通過。
- 它常常沒被發現。純文字中間改了一個位元組仍然可以讀,寬容的影像讀取器只會照樣畫出來,於是本該損毀的檔案被接受了。
- 它沒有說明應該發生什麼。檔案只是一堆位元組,之後讀這個測試的人只能猜測當初想要的是接受還是拒絕。
你會得到什麼
損毀的檔案仍然是你要求的大小
檔案先正常產生,再在寫入磁碟的途中被弄壞。它保持你要求的大小,同一個指令再次寫出的位元組也完全相同。
tfg generate --format png --size 2mb --damage zero-head --out ./out
tfg generate --format pdf --size 1mb --count 5 --damage zero-head:bytes=16 --out ./broken
設定寫在冒號之後。該選項可以重複,損毀依你寫下的順序逐一套用。它適用於全部 26 種格式。
它能做什麼
有哪些損毀方式?
這是程式印出的清單,在建置本頁時從程式中讀取。tfg damage 印出的是同一份清單,tfg damage <id>
則說明其中某一項接受什麼設定。
| 損毀方式 | 對位元組做了什麼 | 最小檔案 | 設定 |
|---|---|---|---|
zero-head |
用零覆蓋檔案開頭的若干位元組,長度保持不變。大多數讀取器最先看的就是那裡,所以幾乎任何東西都會發現這種損毀。 | 8 | bytes |
zero-head
把檔案開頭寫成零。大多數讀取器最先看的就是那裡,也就是說明檔案是什麼的簽章和檔頭,所以幾乎任何讀取器都會發現。純文字和日誌沒有簽章,同樣會被拒絕,因為一串零位元組不是文字。低於四個位元組時,有些格式產生的損毀沒有任何讀取器會抱怨,這就是該設定從四開始的原因。
清單怎麼說
一份說明應發生什麼的清單
每個損毀的檔案都會得到一筆記錄,說明你的系統應該拒絕它,損毀方式記在旁邊:
"expected": {
"outcome": "reject",
"reason": "content_malformed",
"confidence": "certain"
},
"damage": [
{
"type": "zero-head",
"settings": {
"bytes": "8"
}
}
]
有兩種請求會在寫入任何內容之前被拒絕,因為各自都會在磁碟上留下一個清單描述有誤的檔案:
- 比損毀所需更小的檔案,它會原樣輸出
-
在損毀旁邊寫
expected: accept,因為沒有什麼能滿足它。如果你的系統本應修復檔案,請寫sanitize,如果你要問的恰恰就是這一點,請寫unspecified
在配方中
一次執行中的完好檔案和損毀檔案
把兩者放進同一個配方,清單就帶有每個檔案的預期,測試因此不需要一份說明哪個是哪個的列表:
version: 1
targets:
- id: healthy
format: pdf
size: 1mb
expected: accept
- id: broken
format: pdf
size: 1mb
damage:
- zero-head
在測試中
把它變成測試
測試讀取清單,檢查實際發生的是否就是所宣告的。它不需要檔名列表:
import json, os
directory = "healthy-and-broken"
manifest = json.load(open(os.path.join(directory, "manifest.json")))
for entry in manifest["files"]:
response = upload(os.path.join(directory, entry["path"]))
if entry["expected"]["outcome"] == "reject":
assert not response.ok
else:
assert response.ok
好的拒絕是乾淨的拒絕。一則說明錯在哪裡的訊息,就是你想要的答案。伺服器錯誤、卡死或只儲存了一半的檔案,正是這個測試要找出來的缺陷。
接下來
從這裡去哪裡
- upload-validation 預設集向表單提出另外兩個問題,即大小和類型。
- CI 中的測試檔案會在流程裡執行這樣的配方。
-
文件包含
tfg generate的每個選項。