
Ваш застосунок для Mac отримує доступ до мікрофона — і забуває його при кожному запуску
GeekBye попросив доступ до мікрофона, ви його надали, і все запрацювало. Наступний запуск: доступу немає. А в Системних параметрах → Мікрофон застосунок узагалі не з’явився. Винуватцем виявилася функція безпеки macOS, що тихо запускає застосунок зі шляху, який зникає — ось діагноз і виправлення в одне натискання.
Ось вада, через яку починаєш сумніватися у власних очах. Ви встановлюєте GeekBye, він просить доступ до мікрофона, ви натискаєте Дозволити, і транскрипція працює. Чудово. Ви закриваєте, відкриваєте наступного ранку — а він просить доступ до мікрофона знову. Ви йдете в Системні параметри → Конфіденційність і безпека → Мікрофон, щоб виправити це вручну, а GeekBye узагалі немає в списку. Не заборонений. Не дозволений. Просто відсутній, наче він ніколи й не питав.
Кожен окремий шматочок виглядав правильним. Запит був справжнім. Дозвіл працював у ту мить. Застосунок був належно підписаний і нотаризований із правильними рядками використання. І все ж наданий дозвіл випаровувався при кожному запуску. GeekBye v2.0.6 це виправив — а першопричина в одній із найпідступніших речей, які macOS робить, щоб вас захистити.
Функція, що ховала застосунок від нього самого
Винуватець — macOS App Translocation, функція безпеки Gatekeeper. Коли ви завантажуєте застосунок і запускаєте його прямо з DMG чи з папки ~/Downloads — усюди, де він усе ще «в карантині» — macOS насправді запускає його не звідти, де ви його бачите. Він прозоро копіює його у випадковий шлях лише для читання глибоко в /private/var/folders/.../AppTranslocation/… і запускає цю копію. Це хороший захист: він заважає шкідливому завантаженню підміняти файли поруч із собою.
Але ось зіткнення. Система дозволів macOS (TCC — те, що відстежує, хто може використовувати ваш мікрофон, камеру, екран) ідентифікує застосунок за його шляхом та ідентичністю коду. Коли застосунок транслоковано, цей шлях випадковий і тимчасовий. Тож коли ви надаєте доступ до мікрофона, macOS сумлінно записує надання — до шляху, якого не буде при наступному запуску. Відкриваєте застосунок знову, macOS транслокує його в інший випадковий шлях, бачить застосунок, про який у нього немає запису, і питає знову. А оскільки цей фантомний шлях ніколи не буває стабільним місцем, застосунок так і не отримує постійний рядок у Системних параметрах → Мікрофон.
Застосунок надавав дозвіл привиду.
Саме тому з цим стикалися лише деякі люди. Якщо ваша копія GeekBye уже жила в /Applications — бо ви перетягнули її туди або бо вона потрапила туди через автооновлення — немає карантину, немає транслокації, є стабільний шлях, і все зберігається ідеально. Вада була невидимою для нас і для всіх, хто пройшов першу інсталяцію, а це саме той тип вади, що виживає найдовше.
Виправлення: дайте застосунку справжній дім
Раз уся проблема — у нестабільному шляху, виправлення в тому, щоб перемістити застосунок на стабільний. v2.0.6 виявляє, коли GeekBye працює транслокованим (або просто працює поза /Applications), і пропонує в одне натискання «Move to Applications» — використовуючи виклик переміщення macOS, що копіює бандл у /Applications і перезапускає його звідти. З цього моменту застосунок має фіксовану ідентичність: надання мікрофона тримається, запис екрана тримається, і GeekBye нарешті з’являється в Системних параметрах там, де ви цього очікуєте.
Запит ввічливий щодо цього. Він пропонує Move to Applications, Not Now і Don't Ask Again — і запам’ятовує останній вибір, тож застосунок ніколи не докучає тому, хто має свідому причину запускати його з іншого місця. Рішення про те, чи показувати запит узагалі, ізольоване в маленькі чисті функції (чи транслокована ця збірка? чи вона поза /Applications? чи вимкнув її користувач?), тож логіка покрита юніт-тестами без запуску справжньої нотаризованої збірки на справжньому карантинному томі.
Той самий реліз спростив і сам досвід роботи з дозволами. GeekBye раніше викидав кастомне, саморобне вбудоване вікно дозволів — пару сотень рядків інтерфейсу, що намагалися відтворити те, що ОС і так робить добре. v2.0.6 видалив його й опертий на нативний запит дозволів macOS, підкріплений тихим неблокуючим банером, що з’являється лише тоді, коли потрібного дозволу справді бракує. Менше коду, і поведінка, яку користувачі вже впізнають, бо будь-який інший застосунок для Mac працює так само.
Частина, якою я пишаюся найбільше: ми довели це, перш ніж повірили
Було б легко вгадати транслокацію й випустити виправлення. Натомість реліз спершу випустив стартову діагностику: при запуску GeekBye тепер повідомляє власний шлях виконуваного файлу й те, чи транслокований він, чи знаходиться всередині /Applications, а також поточний статус дозволів на мікрофон та екран. Ця телеметрія перетворила правдоподібну теорію на підтверджену — виробничі дані показали, що зачеплені сесії справді працювали з транслокованих шляхів поза /Applications, точно як передбачалося.
Цей порядок важливий. У «запит з’являється, але дозвіл не тримається» є кілька можливих пояснень — проблема з підписом, відсутній рядок використання, питання з entitlement, примха бази TCC. Ми виключили причини на рівні API (шлях запиту був очевидно коректним), а потім дозволили реальним даним указати на шар ідентичності/шляху, замість того щоб випускати виправлення навмання й сподіватися, що звернення в підтримку припиняться.
Три речі, яких навчає ця вада
- Дозвіл, який запитується, але не зберігається — це проблема ідентичності, а не API. Якщо код запиту коректний, а надання все одно зникає, перестаньте переписувати запит. Спитайте, до якого шляху та ідентичності коду ОС прив’язує надання — і чи стабільна ця ідентичність між запусками.
- Випускайте діагностику разом із (або перед) виправленням. Кілька полів — звідки я працюю, який мій статус дозволів — перетворили обґрунтований здогад на перевірену першопричину й сказали нам точно, які користувачі були зачеплені. Оснастіть інструментами межу, яку підозрюєте, перш ніж її латати.
- Вади, що ховаються від розробників — це ті, що працюють у «неправильному» середовищі. Наша жила в
/Applicationsна кожній машині розробника, тож була структурно невидимою для нас, потрапляючи при цьому в нових користувачів. Коли звернення не відтворюється, перше питання — що відрізняється в місці, де воно працює, а не чи не помиляється користувач.
GeekBye v2.0.6 випустив запит переміщення й діагностику разом. Про ширшу дугу надійності, у яку це вписується, див. скільки насправді коштує версія 2 (v2.0.0) і чому запис екрана захоплює не той монітор (v2.0.10) — ще одна вада, що виринула лише в конкретному середовищі. Про реліз із дрібними деталями по сусідству — спокійне ПЗ: виправлення мерехтіння й чип режиму відповіді (v2.0.3 + v2.0.5).