Steven
Steven2 นาทีที่อ่าน

แอป Mac ของคุณอนุญาตให้ใช้ไมโครโฟน — แล้วลืมมันทุกครั้งที่เปิด

GeekBye ขอสิทธิ์ไมโครโฟน คุณกดอนุญาต และมันก็ทำงานได้ พอเปิดครั้งถัดไป: หายไป และแอปไม่เคยโผล่ใน System Settings → Microphone เลยด้วยซ้ำ ตัวการคือฟีเจอร์ความปลอดภัยของ macOS ที่แอบรันแอปจากพาธที่หายวับไป — นี่คือการวินิจฉัยและการแก้แบบกดครั้งเดียวจบ

macOS
สิทธิ์การเข้าถึง
วิศวกรรม
GeekBye Releases
แอป Mac ของคุณอนุญาตให้ใช้ไมโครโฟน — แล้วลืมมันทุกครั้งที่เปิด

นี่คือบั๊กที่ทำให้คุณสงสัยสายตาตัวเอง คุณติดตั้ง GeekBye มันขอสิทธิ์ไมโครโฟน คุณกด Allow แล้วการถอดเสียงก็ทำงาน เยี่ยม คุณปิดแอป เปิดมันใหม่ในเช้าวันถัดมา — แล้วมันก็ขอสิทธิ์ไมโครโฟนอีกครั้ง คุณเข้าไปที่ System Settings → Privacy & Security → Microphone เพื่อแก้ด้วยตัวเอง แต่ GeekBye ไม่มีอยู่ในรายการเลยด้วยซ้ำ ไม่ได้ถูกปฏิเสธ ไม่ได้ถูกอนุญาต แค่หายไปเฉยๆ ราวกับว่ามันไม่เคยขอ

ทุกชิ้นส่วนแยกกันดูถูกต้องหมด หน้าต่างขอสิทธิ์เป็นของจริง สิทธิ์ก็ทำงานได้ในตอนนั้น แอปถูกเซ็นและ notarize อย่างถูกต้องพร้อม usage string ที่ครบ กระนั้นการอนุญาตก็ระเหยหายไปทุกครั้งที่เปิด GeekBye v2.0.6 แก้มันแล้ว — และต้นตอคือหนึ่งในกลไกที่แนบเนียนที่สุดที่ macOS ทำเพื่อปกป้องคุณ

ฟีเจอร์ที่ซ่อนแอปจากตัวมันเอง

ตัวการคือ macOS App Translocation ฟีเจอร์ความปลอดภัยของ Gatekeeper เมื่อคุณดาวน์โหลดแอปแล้วรันมันตรงจาก DMG หรือจากโฟลเดอร์ ~/Downloads — ที่ไหนก็ตามที่มันยังถูก "quarantine" อยู่ — macOS ไม่ได้รันมันจากที่ที่คุณเห็นจริงๆ มันจะคัดลอกแอปแบบโปร่งใสไปยังพาธแบบสุ่มที่อ่านได้อย่างเดียวลึกอยู่ใต้ /private/var/folders/.../AppTranslocation/… แล้วรันสำเนาตัวนั้น นี่เป็นการป้องกันที่ดี: มันหยุดไม่ให้ไฟล์ที่ดาวน์โหลดมาแบบประสงค์ร้ายไปยุ่งกับไฟล์ที่อยู่ข้างๆ ตัวมันเอง

แต่นี่คือจุดที่ชนกัน ระบบสิทธิ์ของ macOS (TCC — ตัวที่คอยติดตามว่าใครใช้ไมค์ กล้อง หน้าจอของคุณได้) ระบุตัวแอป จากพาธและตัวตนของโค้ด (code identity) เมื่อแอปถูก translocate พาธนั้นจะเป็นแบบสุ่มและชั่วคราว ดังนั้นเมื่อคุณอนุญาตให้ใช้ไมโครโฟน macOS ก็บันทึกการอนุญาตอย่างซื่อสัตย์ — กับพาธที่จะไม่มีอยู่แล้วในการเปิดครั้งถัดไป พอเปิดแอปใหม่ macOS ก็ translocate มันไปยังพาธสุ่มที่ต่างออกไป เห็นแอปที่มันไม่มีบันทึกอยู่เลย จึงถามอีกครั้ง และเพราะพาธผีตัวนั้นไม่เคยเป็นตำแหน่งที่คงที่ แอปจึงไม่เคยได้บรรทัดถาวรใน System Settings → Microphone

แอปกำลังอนุญาตสิทธิ์ให้กับผี

นี่ก็เป็นเหตุผลว่าทำไมมีแค่บางคนที่เจอ ถ้าสำเนา GeekBye ของคุณอยู่ใน /Applications อยู่แล้ว — เพราะคุณลากมันไปไว้ที่นั่น หรือเพราะมันไปอยู่ที่นั่นผ่านการอัปเดตอัตโนมัติ — ก็ไม่มี quarantine ไม่มี translocation มีพาธที่คงที่ และทุกอย่างถูกจดจำอย่างสมบูรณ์แบบ บั๊กนี้จึงมองไม่เห็นทั้งสำหรับเราและสำหรับใครก็ตามที่ผ่านการติดตั้งครั้งแรกไปแล้ว ซึ่งเป็นบั๊กประเภทที่อยู่รอดได้นานที่สุดพอดี

การแก้: มอบบ้านจริงให้แอป

เพราะปัญหาทั้งหมดคือพาธที่ไม่คงที่ การแก้จึงคือการพาแอปไปอยู่บนพาธที่คงที่ v2.0.6 ตรวจจับเมื่อ GeekBye กำลังรันแบบ translocated (หรือแค่รันอยู่นอก /Applications) แล้วเสนอปุ่ม "Move to Applications" แบบคลิกเดียว — โดยใช้คำสั่งย้ายตำแหน่งของ macOS ที่คัดลอก bundle เข้าไปใน /Applications แล้วรีสตาร์ทแอปจากที่นั่น นับจากจุดนั้นเป็นต้นไป แอปก็มีตัวตนที่ตายตัว: สิทธิ์ไมโครโฟนถูกจดจำ สิทธิ์บันทึกหน้าจอถูกจดจำ และในที่สุด GeekBye ก็ปรากฏใน System Settings ในที่ที่คุณคาดหวัง

หน้าต่างขอสิทธิ์นั้นสุภาพ มันเสนอ Move to Applications, Not Now และ Don't Ask Again — และมันจดจำตัวเลือกล่าสุดเอาไว้ เพื่อไม่ให้แอปคอยรบกวนคนที่มีเหตุผลจงใจที่จะรันมันจากที่อื่น การตัดสินใจว่าควรจะแสดงหน้าต่างนี้ไหมถูกแยกออกมาเป็นฟังก์ชันเล็กๆ ที่บริสุทธิ์ (build นี้ถูก translocate ไหม? มันอยู่นอก /Applications ไหม? ผู้ใช้ปิดมันไปหรือเปล่า?) ตรรกะนี้จึงถูก unit-test ได้โดยไม่ต้องเปิด build ที่ notarize จริงบนวอลุ่มที่ถูก quarantine จริง

รีลีสเดียวกันนี้ยังทำให้ประสบการณ์การขอสิทธิ์เองเรียบง่ายขึ้นด้วย GeekBye เคยเด้งหน้าต่างขอสิทธิ์ในแอปแบบทำเองขึ้นมา — UI สองสามร้อยบรรทัดที่พยายามเลียนแบบสิ่งที่ OS ทำได้ดีอยู่แล้ว v2.0.6 ลบมันทิ้งและหันไปพึ่ง หน้าต่างขอสิทธิ์เนทีฟของ macOS โดยมีแบนเนอร์เงียบๆ ที่ไม่ขวางการทำงานคอยหนุน ซึ่งจะโผล่ขึ้นเฉพาะเมื่อสิทธิ์ที่จำเป็นขาดหายไปจริงๆ โค้ดน้อยลง และเป็นพฤติกรรมที่ผู้ใช้คุ้นเคยอยู่แล้วเพราะแอป Mac ตัวอื่นทุกตัวก็ทำงานแบบเดียวกัน

ส่วนที่ผมภูมิใจที่สุด: เราพิสูจน์มันก่อนที่จะเชื่อ

มันคงง่ายที่จะเดาว่าเป็น translocation แล้วปล่อยการแก้ออกไปเลย แต่แทนที่จะทำแบบนั้น รีลีสนี้ปล่อย เครื่องมือวินิจฉัยตอนเริ่มเปิดออกไปก่อน: ตอนเปิดแอป GeekBye จะรายงานพาธของไฟล์รันของตัวเองและว่ามันถูก translocate ไหม อยู่ใน /Applications ไหม และสถานะสิทธิ์ไมโครโฟนกับหน้าจอในปัจจุบัน เทเลเมทรีนั้นเปลี่ยนทฤษฎีที่ดูมีเหตุผลให้กลายเป็นทฤษฎีที่ยืนยันแล้ว — ข้อมูลจริงในโปรดักชันแสดงให้เห็นว่าเซสชันที่ได้รับผลกระทบนั้น อันที่จริงกำลังรันจากพาธที่ถูก translocate อยู่นอก /Applications ตรงตามที่ทำนายไว้เป๊ะ

ลำดับนี้สำคัญ "หน้าต่างขอสิทธิ์โผล่แต่สิทธิ์ไม่ถูกจดจำ" มีคำอธิบายที่เป็นไปได้หลายอย่าง — ปัญหาการเซ็น usage string ที่ขาด ปัญหา entitlement หรือความแปลกของฐานข้อมูล TCC เราตัดสาเหตุระดับ API ออกไป (เส้นทางการขอสิทธิ์นั้นถูกต้องอย่างพิสูจน์ได้) แล้วปล่อยให้ข้อมูลจากโลกจริงชี้ไปที่ชั้นตัวตน/พาธแทน แทนที่จะปล่อยการแก้ที่เต็มไปด้วยความหวังออกไปแล้วภาวนาให้ตั๋วซัพพอร์ตหยุด

สามสิ่งที่บั๊กนี้สอน

  1. สิทธิ์ที่เด้งขอแต่ไม่ถูกจดจำคือปัญหาเรื่องตัวตน ไม่ใช่ปัญหาเรื่องAPI ถ้าโค้ดการขอถูกต้องแล้วแต่การอนุญาตยังหายไป หยุดเขียนโค้ดการขอใหม่ ถามว่า OS กำลังผูกการอนุญาตไว้กับพาธและ code identity ตัวไหน — และตัวตนนั้นคงที่ข้ามการเปิดแต่ละครั้งไหม
  2. ปล่อยเครื่องมือวินิจฉัยไปพร้อม (หรือก่อน) การแก้ ฟิลด์ไม่กี่ตัว — ฉันกำลังรันจากที่ไหน สถานะสิทธิ์ของฉันเป็นอย่างไร — เปลี่ยนการเดาที่มีหลักการให้กลายเป็นต้นตอที่ยืนยันแล้ว และบอกเราได้ชัดว่าผู้ใช้กลุ่มไหนได้รับผลกระทบ ติดตั้งเครื่องมือวัดที่ขอบเขตที่คุณสงสัยก่อนที่จะแพตช์มัน
  3. บั๊กที่ซ่อนจากนักพัฒนาคือบั๊กที่รันอยู่ในสภาพแวดล้อมที่ "ผิด" ของเราอาศัยอยู่ใน /Applications บนทุกเครื่องพัฒนา บั๊กจึงมองไม่เห็นเชิงโครงสร้างสำหรับเรา ในขณะที่มันเล่นงานผู้ใช้ครั้งแรก เมื่อรายงานหนึ่งทำซ้ำไม่ได้ คำถามแรกคืออะไรต่างกันเรื่องที่ที่มันรัน ไม่ใช่ผู้ใช้เข้าใจผิดหรือเปล่า

GeekBye v2.0.6 ปล่อยหน้าต่างย้ายตำแหน่งและเครื่องมือวินิจฉัยไปพร้อมกัน สำหรับส่วนโค้งเรื่องความน่าเชื่อถือที่ใหญ่กว่าซึ่งเรื่องนี้เป็นส่วนหนึ่ง ดูบันทึกว่าเวอร์ชัน 2 ต้องใช้อะไรจริงๆ (v2.0.0) และทำไมการบันทึกหน้าจอถึงจับมอนิเตอร์ผิดตัว (v2.0.10) — อีกบั๊กหนึ่งที่โผล่มาเฉพาะในสภาพแวดล้อมจำเพาะ สำหรับรีลีสเรื่องรายละเอียดเล็กๆ ข้างเคียง ดูซอฟต์แวร์ที่สงบนิ่ง: การแก้อาการกระพริบกับชิปโหมดตอบ (v2.0.3 + v2.0.5)

บทความที่เกี่ยวข้อง

ซอฟต์แวร์ที่นิ่ง: กำจัดปุ่มสวิตช์ที่กะพริบ และสอนแชตให้บอกว่ากำลังอยู่ในโหมดไหน
Steven
Steven2 นาทีที่อ่าน

ซอฟต์แวร์ที่นิ่ง: กำจัดปุ่มสวิตช์ที่กะพริบ และสอนแชตให้บอกว่ากำลังอยู่ในโหมดไหน

GeekBye ปล่อยอัปเดตเล็ก ๆ สองเวอร์ชัน ไม่มีฟีเจอร์เด่นอะไรเลย มีแค่ปุ่มสวิตช์ในหน้าตั้งค่าที่เลิกกะพริบ กับผู้ช่วยแชตที่ในที่สุดก็บอกคุณได้แล้วว่ามันตอบในโหมดประชุมหรือโหมดเขียนโค้ด นี่คือราคาที่แท้จริงของคำว่า "ซอฟต์แวร์ที่นิ่ง" ทีละรายละเอียด

ผลิตภัณฑ์
ดีไซน์
วิศวกรรม
เมื่อไฟร์วอลล์บล็อก WebSocket แล้วการถอดเสียงเรียลไทม์จะเป็นยังไง
Steven
Steven3 นาทีที่อ่าน

เมื่อไฟร์วอลล์บล็อก WebSocket แล้วการถอดเสียงเรียลไทม์จะเป็นยังไง

เครือข่ายองค์กรชอบปล่อย HTTPS ให้ผ่าน แต่แอบฆ่า WebSocket upgrade ทิ้งเงียบๆ ซึ่งทำให้การถอดเสียงเรียลไทม์พังแบบไร้เสียง GeekBye v2.0.8 จะ fall back ไปยัง transport แบบ HTTPS ล้วนโดยอัตโนมัติ — และระหว่างที่ทำมันออกมา เราก็เจอบั๊กที่จะทำให้ทั้งฟีเจอร์นี้ไร้ประโยชน์ไปเลย

ถอดเสียง
เครือข่าย
วิศวกรรม
ทำไมการบันทึกหน้าจอถึงจับมอนิเตอร์ผิดตัว (และวิธีที่เราแก้)
Steven
Steven2 นาทีที่อ่าน

ทำไมการบันทึกหน้าจอถึงจับมอนิเตอร์ผิดตัว (และวิธีที่เราแก้)

บนเครื่องที่ต่อสองจอ GeekBye บันทึกและแคปหน้าจอหลักเสมอ ไม่ว่าคุณจะทำงานอยู่บนจอไหน การแก้คือฟังก์ชันเล็กๆ ตัวเดียว — แต่เวอร์ชันแรกของมันผิด และ code review จับได้ว่าทำไม

บันทึกหน้าจอ
หลายจอ
วิศวกรรม