保持敏感資料的安全和私密是軟體開發的首要任務。應用程式日誌是常見的洩漏媒介之一,需要仔細保護以防止秘密的存在。同樣的擔憂和風險也適用於測試日誌,它可能會洩露密碼或存取權杖。運行 CI 工作流程的工具通常提供一種機制,可以輕鬆屏蔽日誌中的敏感資料。雖然這非常方便、高效且易於使用,但在某些情況下,這可能還不夠。
例如,GitHub Actions 在處理機密方面做得很好。工作流程中定義的任何秘密都會自動從捕獲的輸出中屏蔽掉,這就像一個魅力。然而,與任何 CI 系統一樣,它也有其局限性。如果輸出報告採用不同的路徑(例如儲存到檔案、產生 junit 或傳送到遠端日誌儲存),GitHub Actions 無法檢查內容並掩蓋機密。
此外,測試並不總是在 CI 工作流程中運行,即使如此,秘密可能仍然需要隱藏。想像一下您正在本地運行測試並共享日誌來討論問題。您在不知不覺中將 URL 包含在存取權杖中。
因此,擁有一種處理測試日誌中敏感資料的機制對於各個層級都是至關重要的。最好的方法是直接在測試層級或測試框架本身內實現這一點。這可以確保秘密不會從主要來源洩漏,從而防止它們通過系統向上傳遞。
直接在測試中維護秘密屏蔽可能成本相對較高且容易出錯,而且常常感覺像是一場失敗的戰鬥。例如,假設您需要設計一個以令牌作為參數的 URL。與日誌中的存在相比,此 URL 必須以不同的方式呈現才能在請求中使用。
相較之下,在測試框架內攔截報告產生提供了一個理想的機會來掛鉤流程並修改記錄以消除敏感資料。這種方法對測試是透明的,不需要修改測試程式碼,並且其功能就像 CI 工作流程中的秘密屏蔽功能一樣 — 只需運行它,而無需管理秘密。它使該過程自動化,確保敏感資料受到保護,而不會為測試設定增加額外的複雜性。
這正是 pytest-mask-secrets 所做的,顯然當 pytest 用於測試執行。在其眾多功能中,pytest 提供了豐富且靈活的插件系統。為此,它允許您在生成任何日誌之前、在所有資料都已收集完畢時掛接到該進程。這使得在輸出記錄之前可以輕鬆搜尋並刪除記錄中的敏感值。
為了說明其工作原理,一個簡單的範例將是最有效的。下面是一個簡單的測試,可能不代表真實世界的測試場景,但可以很好地演示 pytest-mask-secrets。
import logging import os def test_password_length(): password = os.environ["PASSWORD"] logging.info("Tested password: %s", password) assert len(password) > 18
在此範例中,有一個可能會失敗的斷言(並且一定會失敗),以及一條包含秘密的日誌訊息。是的,在日誌中包含秘密可能看起來很愚蠢,但請考慮這樣一個場景:您有一個帶有令牌作為參數的 URL,並且啟用了詳細的偵錯日誌記錄。在這種情況下,像 requests 這樣的函式庫可能會無意中以這種方式記錄秘密。
現在進行測試。首先,設定測試所需的秘密:
(venv) $ export PASSWORD="TOP-SECRET"
接下來,執行測試:
(venv) $ pytest --log-level=info test.py ============================= test session starts ============================== platform linux -- Python 3.12.4, pytest-8.3.2, pluggy-1.5.0 rootdir: /tmp/tmp.AvZtz7nHZS collected 1 item test.py F [100%] =================================== FAILURES =================================== _____________________________ test_password_length _____________________________ def test_password_length(): password = os.environ["PASSWORD"] logging.info("Tested password: %s", password) > assert len(password) > 18 E AssertionError: assert 10 > 18 E + where 10 = len('TOP-SECRET') test.py:8: AssertionError ------------------------------ Captured log call ------------------------------- INFO root:test.py:7 Tested password: TOP-SECRET =========================== short test summary info ============================ FAILED test.py::test_password_length - AssertionError: assert 10 > 18 ============================== 1 failed in 0.03s ===============================
預設情況下,秘密值在輸出中出現兩次:一次在捕獲的日誌訊息中,另一次在失敗的斷言中。
但是如果安裝了 pytest-mask-secrets 會怎麼樣?
(venv) $ pip install pytest-mask-secrets
並進行對應配置。它需要知道保存秘密的環境變數清單。這是透過設定 MASK_SECRETS 變數來完成的:
(venv) $ export MASK_SECRETS=PASSWORD
現在,重新執行測試:
(venv) $ pytest --log-level=info test.py ============================= test session starts ============================== platform linux -- Python 3.12.4, pytest-8.3.2, pluggy-1.5.0 rootdir: /tmp/tmp.AvZtz7nHZS plugins: mask-secrets-1.2.0 collected 1 item test.py F [100%] =================================== FAILURES =================================== _____________________________ test_password_length _____________________________ def test_password_length(): password = os.environ["PASSWORD"] logging.info("Tested password: %s", password) > assert len(password) > 18 E AssertionError: assert 10 > 18 E + where 10 = len('*****') test.py:8: AssertionError ------------------------------ Captured log call ------------------------------- INFO root:test.py:7 Tested password: ***** =========================== short test summary info ============================ FAILED test.py::test_password_length - AssertionError: assert 10 > 18 ============================== 1 failed in 0.02s ===============================
現在,在印秘密的地方會出現星號,而不是秘密值。工作已完成,測試報告現已不含敏感數據。
從範例來看,pytest-mask-secrets 似乎沒有比 GitHub Actions 預設所做的更多,這使得這項工作顯得多餘。然而,如前所述,CI 工作流程執行工具僅掩蓋捕獲的輸出中的秘密,而使 JUnit 檔案和其他報告保持不變。如果沒有 pytest-mask-secrets,敏感資料仍然可能會在這些檔案中暴露——這適用於 pytest 產生的任何報告。另一方面,當使用 log_cli 選項時,pytest-mask-secrets 不會屏蔽直接輸出,因此 CI 工作流程的屏蔽功能仍然有用。通常最好結合這兩種工具來確保敏感資料的保護。
就是這樣。感謝您花時間閱讀這篇文章。我希望它為使用 pytest-mask-secrets 增強測試過程的安全性提供了寶貴的見解。
測試愉快!
以上是使用 pytest-mask-secrets 保護測試機密的詳細內容。更多資訊請關注PHP中文網其他相關文章!