paranco

Applicazione per macOS e riga di comando

paranco

Una piccola applicazione per macOS che possiede l'Accesso completo al disco, così i programmi che leggono il suo output non devono averlo.

L'Accesso completo al disco è l'impostazione di privacy di macOS che permette a un programma di leggere le cartelle protette da macOS, come la libreria dei Memo Vocali.

macOS 14 o successivo. MIT.

Il permesso è più grande del bisogno.

Alcune cartelle su un Mac sono protette. Un programma che ne legge una ha bisogno dell'Accesso completo al disco. Questa impostazione non riguarda una sola cartella. Apre in una volta tutto ciò che macOS protegge.

Di solito serve una sola cartella. Un programma che vuole leggere la libreria dei Memo Vocali dovrebbe avere l'Accesso completo al disco per farlo direttamente. Concesso a quel programma, il permesso raggiunge anche ogni interprete o runtime che carica, ogni pacchetto installato accanto e tutto ciò che quei pacchetti caricano dopo. Niente di tutto questo ha chiesto il permesso.

paranco è la cosa più piccola da concedere. Legge una cartella per rotta e scrive in una cartella normale. Al programma che usa i file non viene concesso nulla, e continua a funzionare come prima.

Due comportamenti di macOS che hanno dato forma a questo strumento.

Entrambi sono stati misurati. Il primo spiega perché paranco è un'applicazione e non uno script. Il secondo spiega perché distingue una cartella vuota da una cartella rifiutata.

Primo risultato

Un accesso è attribuito a chi lo ha chiesto.

macOS attribuisce un accesso al processo che lo ha chiesto. Se controlli il Finder o System Events da un terminale, si verifica il permesso del terminale, non il loro. Non si può prendere in prestito un permesso pilotando qualcosa che lo ha.

Quindi il titolare del permesso deve essere un'applicazione con una propria identità, avviata da launchd o da una persona. Quella è Paranco.app. Il suo agente esegue l'eseguibile dell'applicazione con --agent, così una sola concessione serve sia la finestra sia l'agente. La riga di comando esegue lo stesso nucleo da un terminale, quindi le viene attribuito il permesso del terminale. Su una cartella protetta, paranco check risponde che è rifiutata anche dopo aver concesso il permesso all'app. È macOS che attribuisce correttamente l'accesso.

Secondo risultato

Un rifiuto che arriva come cartella vuota.

Se la si chiede tramite System Events, una cartella protetta non risponde di no. Risponde che è vuota.

16

Una cartella normale con sedici file, interrogata tramite System Events. Il numero che ha.

0

La libreria dei Memo Vocali, interrogata nello stesso modo da un processo senza il permesso. Né errore né rifiuto. Zero.

Zero è una risposta plausibile per una libreria, ed è per questo la più fuorviante. Quindi ogni cartella che paranco controlla riceve una di tre risposte, e un rifiuto porta la frase di cui una persona ha bisogno.

RispostaChe cosa significa
leggibileLa cartella si può elencare e il conteggio è reale. Zero qui è uno zero reale.
rifiutataLa cartella c'è, e macOS non permette a questo processo di elencarla. Il messaggio indica la cartella e l'impostazione da cambiare.
mancanteNon c'è nessuna cartella in quel percorso.

macOS permette a un programma di chiedere se una cartella protetta esiste. Ciò che viene rifiutato è l'elenco del contenuto, ed è questo che rende separabili i tre casi.

Una lista, non un file, decide che cosa si può leggere.

Quattro fatti spiegano il progetto: che cosa è la lista, che cosa deve essere una destinazione, che cosa fa la copia e che cosa può ancora fare un programma che gira come te.

La lista è compilata nel programma

Una rotta indica la sua sorgente con un identificatore, mai con un percorso. L'identificatore si risolve solo su Source.builtIn, un elenco scritto nel codice sorgente. Oggi ha una voce, voice-memos. Nulla sul computer può aggiungervi voci: né un file, né la finestra, né la riga di comando. Aggiungere una cartella significa modificare il codice sorgente e ricompilare. Con una firma ad hoc, ricompilare significa anche concedere di nuovo l'Accesso completo al disco, così chi rivede il codice vede la cartella aggiunta e una persona acconsente di nuovo.

Una destinazione si controlla prima dell'uso

Deve essere una cartella reale dentro la tua cartella personale, fuori da ~/Library, non dentro una sorgente, e sul disco stesso di questo Mac e non su uno di rete o rimovibile. Nel suo percorso non può comparire nessun collegamento simbolico, né alcun componente . o ... Tutto questo si decide prima di scrivere un solo byte.

La copia non prende scorciatoie

Ogni file finisce in un file nuovo, creato con O_EXCL e O_NOFOLLOW, con modalità 0600, senza bit di esecuzione e senza attributi copiati. Poi viene rinominato al suo posto. Tutto ciò che c'è già con quel nome, compreso un collegamento simbolico, viene sostituito dal rinominamento e non viene mai seguito.

Che cosa una scrittura abusiva non può ancora fare

Un programma che gira come te può ancora scrivere routes.json. Nulla nel filesystem lo impedisce. Quello che non può fare è indicare una sorgente che questa build non elenca, o dare un percorso come sorgente. Può puntare una rotta a un'altra cartella della tua cartella personale, ma solo a una che superi gli stessi controlli sulla destinazione. Il confine è l'elenco delle sorgenti, non il file.

paranco non ha codice di rete. Il codice sorgente non contiene sessioni URL, socket né controlli di aggiornamento. Non scrive nulla in una sorgente. Mail, Messaggi e Safari non sono nella lista, e non verranno aggiunti.

Rotte, e un agente che le esegue.

Una rotta indica una sorgente con un identificatore, una cartella di destinazione e i file che porta. Non c'è modo di dire «tutto ciò che sta sotto Library». Il programma ha un permesso che potrebbe leggere qualsiasi cosa, e lo usa per leggere una cosa sola.

name          il nome della rotta
sourceID      un identificatore dell'elenco compilato, es. "voice-memos"
destination   una cartella normale, creata se non esiste
extensions    minuscole, senza il punto; vuoto significa l'elenco della sorgente
enabled       una rotta disattivata viene conservata ma non viene mai eseguita

Eseguire una rotta

  • Un file con lo stesso nome e la stessa dimensione è già nella destinazione, e viene lasciato stare.
  • Un file la cui dimensione è diversa viene copiato di nuovo. Succede quando una registrazione era ancora in scrittura la prima volta che è stata vista.
  • Un file la cui dimensione cambia tra due controlli, a due secondi di distanza, è ancora in scrittura. Viene saltato fino alla prossima esecuzione.
  • Una rotta porta i file regolari del primo livello della sua sorgente. Salta i file nascosti e non entra nelle sottocartelle.

La destinazione è il registro. Quello che c'è è quello che è stato portato fuori, quindi non c'è un database che possa restare indietro rispetto alla cartella.

L'agente

Un agente launchd esegue ogni rotta attiva ogni cinque minuti per impostazione predefinita, e una volta al caricamento. Esegue l'eseguibile proprio dell'applicazione con --agent. Ogni rotta viene eseguita una volta, e il processo termina. Stampa una riga solo quando qualcosa è stato copiato, è fallito o è stato rifiutato. Quell'output va in agent.log, accanto a routes.json.

L'agente si installa dalla finestra, non dalla riga di comando. Il permesso appartiene al bundle dell'applicazione, e la finestra è quel bundle.

Il file delle rotte

Le rotte si trovano in ~/Library/Application Support/paranco/routes.json. È un file JSON, quindi si può leggere, confrontare e modificare con strumenti normali. Una rotta aggiunta nella finestra è la stessa rotta che vedono l'agente e la riga di comando. Un programma che legge questo file deve seguire le note di integrazione (in inglese).

Installazione

Ti serve macOS 14 o successivo, e una toolchain Swift 6 (Xcode 16 o i Command Line Tools corrispondenti). Non c'è nulla da scaricare: l'applicazione si compila dal repository con un solo script.

git clone https://github.com/nerln/paranco
cd paranco
./build.sh

Il risultato è Paranco.app nella cartella del repository. La prima compilazione genera anche l'icona, tramite make-icon.sh.

  1. 1

    Concedi l'Accesso completo al disco

    In Impostazioni di Sistema apri Privacy e sicurezza, poi Accesso completo al disco, e aggiungi Paranco.app. Nessun programma che legge i file portati fuori ne ha bisogno. È l'unico permesso di cui hanno bisogno le sue rotte. Altri programmi possono averne bisogno per un lavoro proprio, per esempio il servizio di registro di paratia, che è un programma separato.

  2. 2

    Apri l'applicazione

    open Paranco.app

  3. 3

    Aggiungi una rotta

    Scegli una sorgente dall'elenco, oggi Memo Vocali, e una cartella di destinazione tua, per esempio ~/Recordings. La finestra controlla la sorgente e dice quale delle tre risposte ha ottenuto.

  4. 4

    Installa l'agente

    Installalo dalla finestra. Da quel momento, la destinazione si riempie da sola a ogni esecuzione.

La riga di comando

Compilalo con swift build -c release --product paranco. Il binario finisce in .build/release/paranco. I comandi seguenti usano paranco per quel binario.

paranco sources
paranco add voice-memos ~/Recordings --name memos --ext m4a,wav
paranco routes
paranco check
paranco lift
paranco agent status

Eseguita da un terminale, la riga di comando usa il permesso del terminale. Per questo una sorgente protetta risulta rifiutata lì, anche dopo aver concesso il permesso a Paranco.app. Per le rotte da cartelle protette usa la finestra o l'agente. Installare l'agente non ha una forma da riga di comando, di proposito.

swift test esegue i test del nucleo. Coprono la copia, la regola del «già presente», la sorgente non toccata, i controlli sulla destinazione e la differenza tra una cartella rifiutata e una vuota.

Limiti

Questi limiti sono anche nel README del progetto (in inglese). Leggili prima di fare affidamento su una garanzia.

  1. Solo macOS, per natura. Il permesso e le cartelle protette sono di macOS.
  2. Oggi c'è una sola sorgente nell'elenco, la libreria dei Memo Vocali. Qualsiasi altra rotta verso di essa si può scrivere con paranco add o nel file. Una nuova sorgente richiede una modifica al codice.
  3. Il bundle ha una firma ad hoc. Ogni ricompilazione è un'applicazione nuova per l'elenco della privacy, quindi l'Accesso completo al disco va concesso di nuovo dopo ogni compilazione.
  4. Non c'è ancora un Developer ID, quindi non c'è un download notarizzato. Si compila dal repository.
  5. Da un terminale, la riga di comando usa il permesso del terminale. Non può leggere una sorgente protetta, anche dopo che all'app è stato concesso l'accesso.
  6. Una destinazione non deve essere un volume cifrato mentre quel volume è chiuso, per esempio una cartella protetta da paratia. Il percorso è allora una cartella vuota sul tuo disco, e i file portati lì vengono scritti in chiaro. paranco non può vedere i volumi cifrati.
  7. Una destinazione è una cartella normale. Qualsiasi programma che gira come te può leggerla. macOS chiede un permesso proprio per Scrivania, Documenti e Download.

La famiglia

paranco è uno dei due piccoli programmi dello stesso autore. Entrambi girano sullo stesso Mac. Nessuno dei due chiama l'altro, e paranco non legge i file di paratia.

paratia mantiene cifrate le cartelle che scegli, così che un agente di programmazione non possa leggerle mentre sono bloccate, e tiene un registro di chi apre o modifica i file che tieni sotto controllo. Blocca anche gli agenti mentre una cartella è aperta, e copre due agenti che insieme fanno ciò che nessuno dei due può fare da solo.

paranco ha l'unico permesso che copia i file fuori da una cartella protetta, così nessun programma che legge le copie deve averlo. Copia in un solo senso, lungo le rotte che imposti, e le sue sorgenti sono scritte nel programma e non in un file.

Ogni programma ha una propria voce nell'elenco di macOS, e ciascuno ha bisogno del proprio permesso.

ProgrammaA che cosa serve l'Accesso completo al discoDopo una nuova compilazione
paratia
Paratia.app
Leggere gli eventi del kernel tramite il suo servizio di registroRimuovere la voce, aggiungerla di nuovo, poi reinstallare il servizio
paranco
Paranco.app
Leggere la cartella Memo VocaliRimuovere la voce, poi aggiungere di nuovo Paranco.app

Sono due voci separate. Il permesso di paranco non rende superfluo quello di paratia.

I due programmi condividono un file, routes.json. paratia può leggerlo senza modificarlo, tramite il suo comando paratia registro rotte-paranco, nelle versioni che includono quel comando. Il comando propone di tenere sotto controllo le cartelle in cui scrivono le rotte, così puoi vedere chi apre i file copiati. Senza paratia, paranco non cambia.

Nessuno dei due programmi ha bisogno dell'altro per funzionare.

Per approfondire

Il nome

Un paranco è un sistema di carrucole e funi per sollevare un carico. Il simbolo è una ruota, una fune e un carico. Accanto, paratia è una parete trasversale della nave, a tenuta d'acqua. Entrambi sono termini marinari.