Problem
ConsoleLine(#136 引入)用 fwrite 取代了兩個會拋不可捕捉 ObjC 例外的產出點。但同一個 package 裡還有五個 仍在用 FileHandle.standardError.write(_:):
file:line
何時觸發
Sources/BestASRKit/Engines/ExternalProcessEngine.swift:247
ExternalEngineRegistry.init,由 CommandCore.swift:81 的 CommandCore.live() 無條件 呼叫 → 每一次 transcribe
Sources/BestASRKit/Supply/WeightVerifier.swift:61
verifyBundled,由 ParakeetEngine.swift:117 與 diarization 呼叫;未 pin 的 repo 正常使用就會觸發
Sources/BestASRKit/Engines/AudioNormalizer.swift:47
NormalizedAudio.cleanup(),transcribe 路徑
Sources/BestASRKit/Store/BenchmarkStore.swift:130
migrateLegacyIfPresent(),由 CommandCore.swift:90 的 store.load() 觸發
Sources/BestASRKit/Benchmark/SRTParser.swift:115
benchmark 路徑(note: 前綴)
FileHandle.write(_:) 在寫入失敗時拋出的是 Swift 無法 catch 的 Objective-C 例外 ,所以 2>&- 會把一次成功的執行變成 SIGABRT(exit 134)——transcript 已經落地、Wrote … 已經印出。
實測(等價最小程式,避免動使用者真實的 ~/.bestasr):
-- normal: warning: engines.json entry 'x' is not external-capable — ignored
survived exit=0
-- 2>&- : exit=134 ← SIGABRT
對照組(同一條通道、走 ConsoleLine):
$ bestasr transcribe /nonexistent-abc.wav 2>&- exit=2
Type
bug
為什麼不在 #136 一併修
PR #141 round-4 的 regression 與 requirements 兩個 lens 都認定該 PR 沒有 scope creep ;DA 指出若要求它一併轉換這五處,那才是 creep。#136 的 scope 是「transcribe 的診斷通道」,而那條已經修好。
ConsoleLine 的 doc 現在明說了這個範圍界線,不再讓敘事讀起來像整個 class 都關掉了。
影響評估(動手前該先做的)
不是攻擊面 :2>&- 是 caller 的 fd 選擇,攻擊者施加不了。安全維度僅止於可用性與 exit code 可信度。
但 exit code 會說謊 :transcript 已寫成功卻 exit 134,任何 $? gating 的 caller(本 repo 自己的 skill 樣板就是)會誤判為失敗。
轉換有取捨 :ConsoleLine 忽略寫入失敗(ConsoleLine 忽略 fwrite 的回傳值:磁碟寫滿時警告會被截斷成「看起來完整」的一行 #150 ),所以轉換等於把「大聲死掉」換成「安靜丟失一行」。對 WeightVerifier 這種供應鏈驗證 的警告,安靜丟失是否可接受,需要個別判斷——不該一律照搬。
發現於 PR #141 round-4 verify(security lens F2)。相關:#136 、#150 。
Problem
ConsoleLine(#136 引入)用fwrite取代了兩個會拋不可捕捉 ObjC 例外的產出點。但同一個 package 裡還有五個仍在用FileHandle.standardError.write(_:):Sources/BestASRKit/Engines/ExternalProcessEngine.swift:247ExternalEngineRegistry.init,由CommandCore.swift:81的CommandCore.live()無條件呼叫 → 每一次 transcribeSources/BestASRKit/Supply/WeightVerifier.swift:61verifyBundled,由ParakeetEngine.swift:117與 diarization 呼叫;未 pin 的 repo 正常使用就會觸發Sources/BestASRKit/Engines/AudioNormalizer.swift:47NormalizedAudio.cleanup(),transcribe 路徑Sources/BestASRKit/Store/BenchmarkStore.swift:130migrateLegacyIfPresent(),由CommandCore.swift:90的store.load()觸發Sources/BestASRKit/Benchmark/SRTParser.swift:115note:前綴)FileHandle.write(_:)在寫入失敗時拋出的是 Swift 無法 catch 的 Objective-C 例外,所以2>&-會把一次成功的執行變成 SIGABRT(exit 134)——transcript 已經落地、Wrote …已經印出。實測(等價最小程式,避免動使用者真實的
~/.bestasr):對照組(同一條通道、走
ConsoleLine):Type
bug
為什麼不在 #136 一併修
PR #141 round-4 的 regression 與 requirements 兩個 lens 都認定該 PR 沒有 scope creep;DA 指出若要求它一併轉換這五處,那才是 creep。#136 的 scope 是「
transcribe的診斷通道」,而那條已經修好。ConsoleLine的 doc 現在明說了這個範圍界線,不再讓敘事讀起來像整個 class 都關掉了。影響評估(動手前該先做的)
2>&-是 caller 的 fd 選擇,攻擊者施加不了。安全維度僅止於可用性與 exit code 可信度。$?gating 的 caller(本 repo 自己的 skill 樣板就是)會誤判為失敗。ConsoleLine忽略寫入失敗(ConsoleLine 忽略 fwrite 的回傳值:磁碟寫滿時警告會被截斷成「看起來完整」的一行 #150),所以轉換等於把「大聲死掉」換成「安靜丟失一行」。對WeightVerifier這種供應鏈驗證的警告,安靜丟失是否可接受,需要個別判斷——不該一律照搬。發現於 PR #141 round-4 verify(security lens F2)。相關:#136、#150。