Steven
Steven5 хв читання

Ваш застосунок для Mac отримує доступ до мікрофона — і забуває його при кожному запуску

GeekBye попросив доступ до мікрофона, ви його надали, і все запрацювало. Наступний запуск: доступу немає. А в Системних параметрах → Мікрофон застосунок узагалі не з’явився. Винуватцем виявилася функція безпеки macOS, що тихо запускає застосунок зі шляху, який зникає — ось діагноз і виправлення в одне натискання.

macOS
Дозволи
Інженерія
Релізи GeekBye
Ваш застосунок для Mac отримує доступ до мікрофона — і забуває його при кожному запуску

Ось вада, через яку починаєш сумніватися у власних очах. Ви встановлюєте 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 (шлях запиту був очевидно коректним), а потім дозволили реальним даним указати на шар ідентичності/шляху, замість того щоб випускати виправлення навмання й сподіватися, що звернення в підтримку припиняться.

Три речі, яких навчає ця вада

  1. Дозвіл, який запитується, але не зберігається — це проблема ідентичності, а не API. Якщо код запиту коректний, а надання все одно зникає, перестаньте переписувати запит. Спитайте, до якого шляху та ідентичності коду ОС прив’язує надання — і чи стабільна ця ідентичність між запусками.
  2. Випускайте діагностику разом із (або перед) виправленням. Кілька полів — звідки я працюю, який мій статус дозволів — перетворили обґрунтований здогад на перевірену першопричину й сказали нам точно, які користувачі були зачеплені. Оснастіть інструментами межу, яку підозрюєте, перш ніж її латати.
  3. Вади, що ховаються від розробників — це ті, що працюють у «неправильному» середовищі. Наша жила в /Applications на кожній машині розробника, тож була структурно невидимою для нас, потрапляючи при цьому в нових користувачів. Коли звернення не відтворюється, перше питання — що відрізняється в місці, де воно працює, а не чи не помиляється користувач.

GeekBye v2.0.6 випустив запит переміщення й діагностику разом. Про ширшу дугу надійності, у яку це вписується, див. скільки насправді коштує версія 2 (v2.0.0) і чому запис екрана захоплює не той монітор (v2.0.10) — ще одна вада, що виринула лише в конкретному середовищі. Про реліз із дрібними деталями по сусідству — спокійне ПЗ: виправлення мерехтіння й чип режиму відповіді (v2.0.3 + v2.0.5).

Схожі статті

Анатомія доведення ПЗ до досконалості: як код-рев'ю зловило те, що не змогли тести
Steven
Steven5 хв читання

Анатомія доведення ПЗ до досконалості: як код-рев'ю зловило те, що не змогли тести

Упродовж усієї серії GeekBye v2 повторюється те саме: виправлення проходить кожен тест на машині розробника, а потім код-рев'ю доводить, що воно б не спрацювало майже ні в кого. Ось робочий процес за дев'ятьма релізами — ворота рев'ю, відлови на етапі виправлення і дисципліна «протестуй до відвантаження», яка перетворює «у мене працює» на «працює».

Інженерія
Процес
Код-рев'ю
Одна CSS-змінна, п'ять раундів рев'ю та ланцюжок інструментів Swift, що збрехав
Steven
Steven6 хв читання

Одна CSS-змінна, п'ять раундів рев'ю та ланцюжок інструментів Swift, що збрехав

GeekBye v2.0.7 зробив увесь оверлей регульовано напівпрозорим — що звучить як однорядкова зміна CSS і абсолютно нею не було. Справжня історія — про те, що виловило код-рев'ю: дві поверхні, які відмовлялися згасати, панель запису, що виглядала неправильно за тієї самої прозорості, і скомпільований бінарник, який стрибнув на 672 байти, бо наша власна документація підказала рецензенту неправильну версію Swift.

Інженерія
Дизайн
Збірка
Спокійне програмне забезпечення: як ми прибрали миготливий перемикач і навчили чат казати, у якому він режимі
Steven
Steven6 хв читання

Спокійне програмне забезпечення: як ми прибрали миготливий перемикач і навчили чат казати, у якому він режимі

Два невеликі релізи GeekBye без гучних функцій — лише перемикач у налаштуваннях, який перестав миготіти, і чат-асистент, який нарешті каже, чи відповів він у режимі зустрічі, чи в режимі кодування. Ось скільки насправді коштує «спокійне програмне забезпечення», по одній деталі за раз.

Продукт
Дизайн
Інженерія