Steven
Steven5 min czytania

Twoja aplikacja na Maca dostaje dostęp do mikrofonu — i zapomina o nim przy każdym uruchomieniu

GeekBye poprosił o dostęp do mikrofonu, przyznałeś go, i zadziałało. Następne uruchomienie: zniknął. A aplikacja w ogóle nie pojawiła się w Ustawieniach systemowych → Mikrofon. Winowajcą była funkcja bezpieczeństwa macOS, po cichu uruchamiająca aplikację ze ścieżki, która znika — oto diagnoza i naprawa jednym kliknięciem.

macOS
Uprawnienia
Inżynieria
Wydania GeekBye
Twoja aplikacja na Maca dostaje dostęp do mikrofonu — i zapomina o nim przy każdym uruchomieniu

Oto błąd, który każe wątpić we własne oczy. Instalujesz GeekBye, prosi o dostęp do mikrofonu, klikasz Zezwól, i transkrypcja działa. Świetnie. Zamykasz, otwierasz następnego ranka — a on prosi o dostęp do mikrofonu znowu. Idziesz sprawdzić Ustawienia systemowe → Prywatność i bezpieczeństwo → Mikrofon, żeby naprawić to ręcznie, a GeekBye w ogóle nie ma na liście. Nie odmówiony. Nie dozwolony. Po prostu nieobecny, jakby nigdy nie pytał.

Każdy pojedynczy element wyglądał poprawnie. Monit był prawdziwy. Uprawnienie działało w danej chwili. Aplikacja była prawidłowo podpisana i notaryzowana, z właściwymi opisami użycia. A mimo to przyznane uprawnienie parowało przy każdym uruchomieniu. GeekBye v2.0.6 to naprawił — a przyczyną jest jedna z najbardziej podstępnych rzeczy, jakie macOS robi, by cię chronić.

Funkcja, która ukrywała aplikację przed nią samą

Winowajcą jest macOS App Translocation, funkcja bezpieczeństwa Gatekeepera. Gdy pobierasz aplikację i uruchamiasz ją prosto z DMG lub z folderu ~/Downloads — wszędzie, gdzie wciąż jest „w kwarantannie" — macOS w rzeczywistości nie uruchamia jej tam, gdzie ją widzisz. Przezroczyście kopiuje ją do losowej ścieżki tylko do odczytu głęboko w /private/var/folders/.../AppTranslocation/… i uruchamia kopię. To dobra obrona: powstrzymuje złośliwe pobranie przed manipulowaniem plikami obok siebie.

Ale tu jest kolizja. System uprawnień macOS (TCC — to, co śledzi, kto może używać twojego mikrofonu, kamery, ekranu) identyfikuje aplikację po jej ścieżce i tożsamości kodu. Gdy aplikacja jest translokowana, ta ścieżka jest losowa i tymczasowa. Więc gdy przyznajesz dostęp do mikrofonu, macOS sumiennie zapisuje przyznanie — do ścieżki, która nie będzie istnieć przy następnym uruchomieniu. Otwierasz aplikację ponownie, macOS translokuje ją do innej losowej ścieżki, widzi aplikację, o której nie ma żadnego zapisu, i pyta znowu. A ponieważ ta fantomowa ścieżka nigdy nie jest stabilną lokalizacją, aplikacja nigdy nie zdobywa trwałego wiersza w Ustawieniach systemowych → Mikrofon.

Aplikacja przyznawała uprawnienie duchowi.

To także dlatego trafiało to tylko niektórych ludzi. Jeśli twoja kopia GeekBye już mieszkała w /Applications — bo przeciągnąłeś ją tam, albo dostała się tam przez automatyczną aktualizację — nie ma kwarantanny, nie ma translokacji, jest stabilna ścieżka i wszystko utrzymuje się idealnie. Błąd był niewidoczny dla nas i dla każdego po pierwszej instalacji, co jest dokładnie tym rodzajem błędu, który przetrwa najdłużej.

Naprawa: daj aplikacji prawdziwy dom

Skoro cały problem to niestabilna ścieżka, naprawa polega na umieszczeniu aplikacji na stabilnej. v2.0.6 wykrywa, gdy GeekBye działa translokowany (albo po prostu działa spoza /Applications) i oferuje jednym kliknięciem „Move to Applications" — używając wywołania relokacji macOS, które kopiuje pakiet do /Applications i uruchamia go stamtąd ponownie. Od tego momentu aplikacja ma stałą tożsamość: przyznanie mikrofonu trzyma się, nagrywanie ekranu trzyma się, a GeekBye w końcu pojawia się w Ustawieniach systemowych tam, gdzie się tego spodziewasz.

Monit jest uprzejmy w tej kwestii. Oferuje Move to Applications, Not Now i Don't Ask Again — i pamięta ostatni wybór, więc aplikacja nigdy nie nęka kogoś, kto ma świadomy powód, by uruchamiać ją skądinąd. Decyzja o tym, czy w ogóle pokazać monit, jest wyizolowana w małe, czyste funkcje (czy ta wersja jest translokowana? czy jest poza /Applications? czy użytkownik ją wyciszył?), więc logika jest testowana jednostkowo bez potrzeby uruchamiania prawdziwej notaryzowanej wersji na prawdziwym woluminie w kwarantannie.

To samo wydanie uprościło też samo doświadczenie z uprawnieniami. GeekBye dawniej wyskakiwał niestandardowym, autorskim wewnętrznym oknem uprawnień — kilkaset linii interfejsu próbujących odtworzyć coś, co system i tak robi dobrze. v2.0.6 usunął je i oparł się na natywnym monicie uprawnień macOS, wspartym cichym, nieblokującym banerem, który pojawia się tylko wtedy, gdy wymaganego uprawnienia faktycznie brakuje. Mniej kodu, i zachowanie, które użytkownicy już rozpoznają, bo każda inna aplikacja na Maca działa tak samo.

Część, z której jestem najbardziej dumny: udowodniliśmy to, zanim uwierzyliśmy

Łatwo byłoby zgadnąć translokację i wypuścić naprawę. Zamiast tego wydanie wypuściło najpierw diagnostykę startową: przy uruchomieniu GeekBye raportuje teraz własną ścieżkę pliku wykonywalnego i to, czy jest translokowany, czy jest wewnątrz /Applications, oraz obecny status uprawnień do mikrofonu i ekranu. Ta telemetria zamieniła prawdopodobną teorię w potwierdzoną — dane produkcyjne pokazały, że dotknięte sesje faktycznie działały ze ścieżek translokowanych poza /Applications, dokładnie jak przewidziano.

To uporządkowanie ma znaczenie. „Monit się pojawia, ale uprawnienie się nie trzyma" ma kilka możliwych wyjaśnień — problem z podpisem, brakujący opis użycia, kwestia uprawnień (entitlement), dziwactwo bazy TCC. Wykluczyliśmy przyczyny na poziomie API (ścieżka żądania była wyraźnie poprawna), a potem pozwoliliśmy, by dane z realnego świata wskazały warstwę tożsamości/ścieżki, zamiast wypuszczać naprawę na wiarę i liczyć, że zgłoszenia się skończą.

Trzy rzeczy, których uczy ten błąd

  1. Uprawnienie, które pyta, ale się nie utrzymuje, to problem tożsamości, a nie API. Jeśli kod żądania jest poprawny, a przyznanie i tak znika, przestań przepisywać żądanie. Zapytaj, do jakiej ścieżki i tożsamości kodu system wiąże przyznanie — i czy ta tożsamość jest stabilna między uruchomieniami.
  2. Wypuść diagnostykę z (albo przed) naprawą. Kilka pól — skąd działam, jaki jest mój status uprawnień — zamieniło wykształcone zgadywanie w zweryfikowaną przyczynę i powiedziało nam dokładnie, którzy użytkownicy byli dotknięci. Oprzyrząduj granicę, którą podejrzewasz, zanim ją załatasz.
  3. Błędy, które ukrywają się przed deweloperami, to te działające w „złym" środowisku. Nasz mieszkał w /Applications na każdej maszynie deweloperskiej, więc był strukturalnie niewidoczny dla nas, trafiając jednocześnie użytkowników po raz pierwszy. Gdy zgłoszenie się nie reprodukuje, pierwsze pytanie brzmi co jest inne w miejscu, gdzie to działa, a nie czy użytkownik się myli.

GeekBye v2.0.6 wypuścił monit relokacji i diagnostykę razem. Dla szerszego łuku niezawodności, w którym to się mieści, zobacz ile naprawdę kosztuje wersja 2 (v2.0.0) oraz dlaczego nagrywanie ekranu przechwytuje niewłaściwy monitor (v2.0.10) — inny błąd, który wypłynął tylko w konkretnym środowisku. Dla wydania z drobnymi detalami po sąsiedzku, spokojne oprogramowanie: naprawa migotania i chip trybu odpowiedzi (v2.0.3 + v2.0.5).

Powiązane artykuły

Anatomia dowożenia oprogramowania do perfekcji: jak przegląd kodu wyłapał to, czego testy nie mogły
Steven
Steven6 min czytania

Anatomia dowożenia oprogramowania do perfekcji: jak przegląd kodu wyłapał to, czego testy nie mogły

W całej serii GeekBye v2 powtarza się to samo: poprawka przechodzi każdy test na maszynie programisty, a potem przegląd kodu dowodzi, że zawiodłaby niemal u każdego. Oto workflow stojący za dziewięcioma wydaniami — bramka przeglądu, wychwyty na etapie poprawki i dyscyplina testowania-przed-wysyłką, która zamienia „u mnie działa" w „działa".

Inżynieria
Proces
Przegląd kodu
Jedna zmienna CSS, pięć rund review i toolchain Swift, który skłamał
Steven
Steven6 min czytania

Jedna zmienna CSS, pięć rund review i toolchain Swift, który skłamał

GeekBye v2.0.7 uczyniło całą nakładkę regulowanie przezroczystą — co brzmi jak jednolinijkowa zmiana w CSS i absolutnie nią nie było. Prawdziwa historia to to, co wychwyciło code review: dwie powierzchnie, które odmawiały gaśnięcia, pasek nagrywania, który wyglądał źle przy tej samej przezroczystości, oraz skompilowany plik binarny, który skakał o 672 bajty, bo nasza własna dokumentacja podała recenzentowi błędną wersję Swift.

Inżynieria
Projektowanie
Kompilacja
Spokojne oprogramowanie: jak zabiliśmy migający przełącznik i nauczyliśmy czat mówić, w jakim jest trybie
Steven
Steven6 min czytania

Spokojne oprogramowanie: jak zabiliśmy migający przełącznik i nauczyliśmy czat mówić, w jakim jest trybie

Dwa niewielkie wydania GeekBye, bez żadnej sztandarowej funkcji — tylko przełącznik w ustawieniach, który przestał migać, i asystent czatu, który wreszcie mówi ci, czy odpowiedział w trybie spotkania, czy w trybie kodowania. Oto ile naprawdę kosztuje „spokojne oprogramowanie", jeden szczegół po drugim.

Produkt
Projektowanie
Inżynieria