English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

guest
1 / ?
back to lessons

命名不是發現

你現在已知道七種 MOAD 模式。知道名稱很重要:它讓你能在看到模式時識別它。但在受控課程中的識別與在你從未打開過的代碼庫中的檢測不同。

代碼庫不會標記其缺陷。沉積 MOAD 不會帶有一條說 // O(N²) — fix this 的註釋。雷鳴群不會宣佈自己是緩存丟失踩踏。你通過帶著特定問題閱讀代碼來找到它們:什麼數據結構持有這些值,什麼操作在循環中針對它運行?

檢測是一項不同於識別的技能。 識別說:是的,那個模式是 MOAD-0001。檢測說:讓我找到這個代碼庫中所有那個模式可能存在的地方,無論我是否能看到完整代碼或只是符號名稱。

七種 MOADs:基質、特徵、修復

首次掃描

首先使用 grep。每個 MOAD 都有一個基質:數據結構或 API,其在某些操作附近的存在是值得調查的信號。

MOAD-0001 (沉積):循環中的 List.contains

# Signal: membership test on a list variable inside a loop
grep -rn '.contains(' src/ | grep -v HashSet | grep -v TreeSet
grep -rn 'visited =' src/ | grep -v set | grep -v Set

MOAD-0002 (互纏):跨階段的共享可變標誌

# Signal: static mutable field written by one subsystem, read by another
grep -rn 'static ' src/ | grep -v final | grep -v class | grep -v void

MOAD-0003 (上下文洩漏):池化執行器中的 ThreadLocal

# Signal: ThreadLocal.set() without guaranteed ThreadLocal.remove()
grep -rn 'ThreadLocal' src/
grep -rn 'ThreadLocal.set' src/ -l

MOAD-0004 (已記錄的秘密):日誌輸出中的 HTTP 頭

# Signal: log call with headers variable near auth endpoints
grep -rn 'log.*header' src/
grep -rn 'Authorization' src/ --include='*.log'

MOAD-0005 (雷鳴群):無同步的緩存丟失

# Signal: cache.get() + null check + cache.put() without lock
grep -rn 'cache.get' src/ -A4 | grep 'cache.put'

這些模式產生候選對象,而不是已確認的缺陷。每個候選對象都需要分類:閱讀周圍代碼、驗證數據結構類型、確認操作在規模上運行。

從 MOAD-0001 到 MOAD-0005 中選擇一個。描述你在從未閱讀過的代碼庫中會採取的具體檢測步驟:你要搜索什麼、正命中看起來像什麼,以及是什麼將已確認的缺陷與誤報區分開來。

閱讀代碼以了解複雜性

Grep 找到候選對象。閱讀確認它們。當你打開一個候選文件時,你帶著一個問題閱讀:這個操作的成本是否隨著輸入大小增長?

對於 MOAD-0001,確認協議:

1. 找到外部循環。什麼限制了它的迭代次數?
2. 找到內部操作(.contains、.indexOf、'in')。它針對什麼數據結構運行?
3. 那個數據結構是否隨著驅動外部循環的相同輸入而增長?
4. 如果是:成本是 O(N²) 其中 N = 輸入大小。已確認缺陷。
5. 如果否:內部結構有界(配置、枚舉、小常數)。誤報。

一個訪問 N 個節點的圖遍歷,在每一步檢查 visited 列表:循環和內部數據結構都隨著 N 增長。已確認。

一個請求處理器檢查 5 個管理 IP 的允許清單:允許清單永遠不會隨著請求量增長。誤報。

相同的協議適用於每個 MOAD:識別外部驅動程序、識別內部結構、詢問兩者是否一起擴展。

激增分數:優先考慮你的發現

並非所有已確認的缺陷都值得立即修補。一個在擁有 10,000 個下游依賴項的庫中的 MOAD 的激增分數高於同一 MOAD 在私有內部工具中的激增分數。

激增分數 = 加速 × 入度。 加速:修復在典型生產規模下運行的速度快多少?入度:有多少下游包或服務會在上游合併時自動繼承修復?

一個在 Apache Maven 的依賴項解析器中已確認的 MOAD-0001,在 50,000 個節點的圖上運行,具有 1,000 多個下游 Maven 插件自動繼承更改:激增分數非常高。這個修復屬於你隊列的前面。

一個在沒有依賴項的單用戶 CLI 工具中已確認的 MOAD-0001:激增分數接近零。值得修復,但不急。

工作狂與貪心節點。 一個具有高介數 & 高加速的節點是工作狂:它處理關鍵流 & 當解鎖時將沖洗下游隊列。僅在確認下游容量後修補它。一個具有高出度 & 低加速的節點是貪心:它消耗所有提供給它的東西,感受不到任何痛苦。修補工作狂而不暫存下游容量會在基礎設施規模上創建 MOAD-0005(雷鳴群)。

工廠 DAG:工作狂 & 貪心節點模式

你已在兩個地方確認了 MOAD-0001:(A) 一個擁有 200,000 個活躍項目依賴它的構建工具中的依賴項解析器,在 10,000 節點的依賴項樹上運行;(B) 一個單個公司內部數據管道中的圖實用程序,在 50 節點的圖上運行。比較它們的激增分數。你首先修補哪一個,在披露前你採取什麼步驟?

掃描到合併:一個 MOAD 管道

一個已確認的具有高激增分數的缺陷通過一個管道移動。每個階段產生一個工件。沒有階段是可選的。

scan    → candidate list (grep output, static analysis results)
ticket  → defect description (MOAD number, location, complexity analysis)
patch   → code change (data structure swap, primitive adoption)
test    → unit test (O(1) proof: time the fix at N=100 and N=10,000)
UNDF    → public disclosure post (undefect.com, public domain)
disclose → CVE or CWE reference if security-relevant
PR      → upstream pull request with patch + test + UNDF link
merge   → maintainer acceptance; fix propagates via version bump

每個工件餵下一個階段。沒有測試的補丁無法驗證。沒有披露的測試無法傳播到同一模式的其他實例。沒有上游 PR 的披露將修復困在分支中。

一個 MOAD 帖子(UNDF)是大多數工程師忽略的階段。 他們修復缺陷、提交 PR,並認為自己完成了。但沒有命名帖子的修復意味著將來遇到同一模式的每個工程師必須獨立重新發現問題和修復。一個 MOAD 帖子關閉知識循環:它命名模式、顯示檢測方法、& 鏈接到補丁。未來的研究人員通過搜索模式名稱找到修復。

行星級修補。 一個廣泛使用的庫中的單個 MOAD-0001 修復傳播到導入它的每個項目。一個 MOAD 帖子確保工程師在永遠不會升級該庫的項目中仍然學會修復。兩條路徑並行運行。

編寫缺陷 Ticket

一個好的缺陷 ticket 回答五個問題:

1. 在哪裡:確切的文件、類、函數和行範圍
2. 什麼:數據結構類型和針對它的操作
3. 為什麼:複雜性分析(O(N²) 或更差,定義了 N)
4. 影響:什麼輸入觸發最壞情況行為,以及在什麼規模
5. 修復:要替換的數據結構或基元

一個回答所有五個問題的 ticket 是自包含的:一個從未閱讀過你分析的維護者可以重現你的發現並驗證你的修復。跳過 (3) 或 (4) 的 ticket 要求維護者在合併前重複你的複雜性分析。那個摩擦減少了合併的概率。

信譽複合。 一個第一個 PR 包括清晰的 ticket、設計精良的補丁、& 基準測試被合併。第二個來自同一作者的 PR 受到的審查摩擦更少。第三個 PR 由合併前兩個的維護者審查。開源中的聲譽是一個工件的帳簿:每個接受的補丁為下一個賺取信任。

為一個你期望在圖庫中找到的 MOAD-0001 編寫一個最小的缺陷 ticket。包括:(1) 一個合理的文件/函數名、(2) 數據結構 & 操作、(3) 一個複雜性聲明、(4) 一個典型影響場景、(5) 修復。

閱讀真實候選對象

這是 Python 中真實的 MOAD-0001 候選對象。閱讀它並完成分類協議。

class DependencyResolver:
    def resolve(self, package, resolved=None, seen=None):
        if resolved is None:
            resolved = []
        if seen is None:
            seen = []
        if package in seen:
            return
        seen.append(package)
        for dep in self.registry.get_dependencies(package):
            self.resolve(dep, resolved, seen)
        resolved.append(package)
        return resolved

分類問題:

1. `seen` 是什麼數據結構?
2. 第 6 行針對它運行什麼操作?
3. `seen` 是否隨著輸入大小增長?
4. 驅動遞歸調用的循環是否也隨著輸入大小增長?
5. 這是已確認的 MOAD-0001 還是誤報?
為這段代碼完成五個分類問題。然後寫出單行修復並解釋為什麼它不改變函數的輸出。

你的補丁

一個已確認的具有高激增分數的缺陷需要一個完整的補丁:代碼修復、一個證明改進的測試、& 一個 MOAD 帖子大綱。

測試必須是性能測試,而不是正確性測試。 一個正確性測試在修復前後通過——那就是重點;輸出不改變。一個在兩個輸入大小的性能測試證明改進:

import time

def build_graph(n):
    # n packages, each depending on the previous one
    return {f'pkg{i}': [f'pkg{i-1}'] if i > 0 else [] for i in range(n)}

for n in [100, 1000, 5000]:
    registry = build_graph(n)
    resolver = DependencyResolver(registry)
    start = time.perf_counter()
    resolver.resolve(f'pkg{n-1}')
    elapsed = time.perf_counter() - start
    print(f'n={n}: {elapsed:.4f}s')

在修復前,經過時間隨著 n 二次增長。修復後,它線性增長。打印兩者並在你的 PR 描述中包括數字。

一個 MOAD 帖子大綱涵蓋:模式名稱、基質(Python 依賴項解析器)、檢測方法(grep in seen 其中 seen 開始為 [])、修復、& 指向你的 PR 的鏈接。帖子作為公共域進入 undefect.com。未來搜索 'Python 列表成員資格在循環中慢' 的工程師會找到它。

你已在一個受歡迎的 Python 打包工具中確認 & 修補了 MOAD-0001。在你打開 PR 之前,你在 PR 描述中包括什麼三件事,為什麼每一個對維護者重要?